Java
Micrometer
Prometheus
HTTP
Subprocesses

Micrometer & Prometheus with Java subprocesses that can't expose HTTP

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Prometheus is built around pull-based scraping, so a Java subprocess that cannot expose an HTTP endpoint does not fit the default model cleanly. The important question is whether that subprocess is a short-lived batch job or a long-lived service, because the right answer is different in each case.

For short-lived jobs, Pushgateway can be acceptable. For long-lived subprocesses, it is usually the wrong tool, and you should prefer an architecture where a parent process, sidecar, or alternate exporter path exposes the metrics instead.

Understand the Constraint First

Micrometer's Prometheus registry normally expects something in your process to serve the scrape output.

java
PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
Counter counter = registry.counter("jobs_processed_total");
counter.increment();

That gives you Prometheus-formatted metrics, but by itself it does not solve transport. If the subprocess cannot bind an HTTP port, Prometheus cannot scrape it directly.

So the real design problem is not collecting metrics. It is exporting them in a way that still respects Prometheus' operational model.

Option 1: Pushgateway for Short-Lived Jobs

If the subprocess is ephemeral, such as a batch task or one-shot worker, Pushgateway can work.

java
1import io.micrometer.prometheusmetrics.PrometheusConfig;
2import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
3import io.prometheus.metrics.exporter.pushgateway.PushGateway;
4
5PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
6registry.counter("jobs_processed_total", "job", "thumbnailer").increment();
7
8PushGateway gateway = PushGateway.builder()
9    .address("pushgateway:9091")
10    .job("thumbnailer_job")
11    .build();
12
13gateway.pushAdd(registry.getPrometheusRegistry());

Prometheus then scrapes the Pushgateway instead of the subprocess.

This model is best for jobs that may finish before Prometheus has a chance to scrape them directly.

Why Pushgateway Is Not a General Replacement

A long-lived subprocess that continually pushes to Pushgateway is usually a design smell in Prometheus systems. Pushgateway is not intended to become a generic always-on metrics mailbox for service instances.

Problems with that approach:

  • stale metrics can remain after the subprocess disappears
  • instance lifecycle becomes harder to reason about
  • pull-based service discovery is bypassed
  • cleanup becomes an operational burden

If the subprocess behaves like a service, try to preserve the normal scrape model instead of forcing a push model onto it.

Option 2: Parent Process Aggregation

If the subprocess is launched by a parent Java process, the parent can expose metrics on its behalf.

Example pattern:

  • subprocess records metrics locally or writes them to stdout, a pipe, or shared file
  • parent process reads and aggregates those values
  • parent process exposes one Prometheus endpoint

This works well when the subprocess is more like a worker than an independently managed service.

A minimal parent-side Micrometer example:

java
AtomicInteger activeWorkers = new AtomicInteger();
Gauge.builder("worker_active", activeWorkers, AtomicInteger::get)
     .register(registry);

The important architectural point is that Prometheus still scrapes one stable HTTP endpoint.

Option 3: File or Sidecar-Based Export

Another option is to have the subprocess write metrics somewhere local and let another component expose them.

Examples include:

  • a sidecar or local agent that reads subprocess output and exposes Prometheus metrics
  • a textfile-based collector pattern when the environment supports it
  • alternate telemetry backends such as StatsD or OTLP if Prometheus scraping is fundamentally impossible

This is often cleaner than opening HTTP in every child process, especially when the subprocesses are numerous or very short-lived.

Common Pitfalls

The biggest mistake is assuming that "no HTTP in the subprocess" automatically means "use Pushgateway." That is only a good fit for short-lived batch-style jobs.

Another common issue is exporting metrics from many long-lived subprocesses without a cleanup strategy. Stale series and ambiguous ownership quickly become a problem.

People also focus on Micrometer configuration and miss the real issue, which is transport architecture. Collecting a counter is easy. Making it observable in a Prometheus-friendly way is the harder part.

Finally, do not forget that the subprocess may not need to be independently scraped at all. In many designs, parent-process aggregation is simpler and more correct.

Summary

  • Micrometer can collect metrics even when a subprocess cannot expose HTTP.
  • The real challenge is exporting those metrics in a Prometheus-compatible way.
  • Pushgateway is suitable mainly for short-lived batch jobs.
  • For long-lived subprocesses, prefer parent aggregation or another stable scrape target.
  • Sidecars and file-based collection can work when direct HTTP exposure is unavailable.
  • Choose the transport pattern based on lifecycle, not just on library convenience.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.