Prometheus exporter vs OTLP for Temporal SDK metrics in multi-worker deployments
I just wrote up a detailed comparison of these two approaches, specifically for the case where you run multiple Temporal workers on the same host (bare metal, PM2, systemd).
The core issue is that the Prometheus exporter starts an in-process HTTP server. Scale to 2+ workers on the same machine → every worker tries to bind:9464 → EADDRINUSE. You can assign unique ports per worker, but now your Prometheus scrape config is tightly coupled to your process management.
The alternative: OTLP push to a shared OpenTelemetry Collector. All workers push to grpc://localhost:4317; the collector aggregates and serves Prometheus text format on:9464. One scrape target regardless of worker count—no port management.
The post includes:
\- Working OTel Collector config (OTLP receiver → batch processor → Prometheus exporter)
\- Docker Compose with proper resource limits
\- PM2 ecosystem config with per-worker service names
\- Startup guard script so the collector doesn't fail silently
\- Honest discussion about metrics loss when the collector is down
\- Comparison table of both approaches
Would be interested to hear what others are using. I know K8s changes the equation since each worker is its own pod with its own port — Prometheus operator handles that well. But for bare metal / PM2 users, OTLP has been a big improvement.
TLDR: Prometheus exporter for single workers/Docker/K8s, OTLP for multi-worker on same host.
(GPT has been used to write this body.)