Prometheus is an excellent systems monitoring and alerting toolkit, which uses a pull model for collecting metrics. The pull model is problematic when a firewall separates a Prometheus server and its metrics endpoints.
Prometheus Proxy enables Prometheus to scrape metrics endpoints running behind a firewall and preserves the native pull-based model architecture.
- Architecture
- Quick Start
- Building from Source
- Configuration Examples
- Docker Usage
- Advanced Features
- Monitoring & Observability
- Configuration Options
- Security & TLS
- Troubleshooting
- Documentation
- License
π **Docs site: ** Architecture for the full component breakdown and request flow, and the Glossary for the core terms used throughout the docs.
The prometheus-proxy runtime comprises two services:
proxy: runs in the same network domain as Prometheus server (outside the firewall) and proxies calls from Prometheus to theagentbehind the firewall.agent: runs in the same network domain as all the monitored hosts/services/apps (inside the firewall). It maps the scraping queries coming from theproxyto the actual/metricsscraping endpoints of the hosts/services/apps.
Prometheus Proxy solves the firewall problem by using a persistent gRPC connection initiated from inside the firewall.
Here's a simplified network diagram of how the deployed proxy and agent work:
Endpoints running behind a firewall require a prometheus-agent (the agent) to be run inside the firewall. An agent can
run as a stand-alone server, embedded in another java server, or as a java agent. Agents connect to
a prometheus-proxy (the proxy) and register the paths for which they will provide data. One proxy can work with one or
many
agents.
-
π Proxy - Runs outside the firewall alongside Prometheus
- HTTP server (port 8080) - Serves metrics to Prometheus
- gRPC server (port 50051) - Accepts agent connections
- Service discovery support for dynamic targets
-
π Agent - Runs inside the firewall with monitored services
- Connects to proxy via gRPC (outbound connection only)
- Scrapes local metrics endpoints
- Registers available paths with proxy
- β Firewall-friendly - Only requires outbound connection from agent
- β Preserves pull model - Prometheus continues to pull as normal
- β High performance - Built with Kotlin coroutines and gRPC
- β Secure - Optional TLS with mutual authentication
- β Scalable - One proxy supports many agents
- β Zero changes to existing Prometheus configuration patterns
π **Docs site: ** Quick Start guide walks through these steps with more detail.
Requirements: Java 17 or newer
-
Download the latest proxy and agent JAR files from releases
-
Start the proxy (runs outside the firewall with Prometheus):
java -jar prometheus-proxy.jar
-
Start the agent (runs inside the firewall with your services):
java -jar prometheus-agent.jar \ -Dagent.proxy.hostname=mymachine.local \ --config https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/myapps.conf # or use --proxy option java -jar prometheus-agent.jar \ --proxy mymachine.local \ --config https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/myapps.conf -
Configure Prometheus to scrape from the proxy at
http://mymachine.local:8080 -
Verify it works with:
curl -s http://mymachine.local:8080/app1_metrics | head # or, if SD is enabled curl -s http://mymachine.local:8080/discovery | jq '.'
If you prefer to build the project from source:
-
Clone the repository:
git clone https://github.com/pambrose/prometheus-proxy.git cd prometheus-proxy -
Build the fat JARs:
./gradlew agentJar proxyJar
agentJarandproxyJarare dedicatedShadowJartasks; the defaultshadowJartask is disabled so it cannot produce a third redundant fat jar. -
The JARs will be available in
build/libs/:
build/libs/prometheus-proxy.jarbuild/libs/prometheus-agent.jar
# Start proxy
docker run --rm -p 8080:8080 -p 50051:50051 pambrose/prometheus-proxy:3.2.0
# Start agent
docker run --rm \
--env AGENT_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \
pambrose/prometheus-agent:3.2.0π **Docs site: ** Agent configuration and Proxy configuration document every config key in depth.
If the prometheus-proxy were running on a machine named mymachine.local and the
agent.pathConfigs value in
the myapps.conf
config file had the contents:
agent {
pathConfigs: [
{
name: "App1 metrics"
path: app1_metrics
labels: "{\"key1\": \"value1\", \"key2\": 2}"
url: "http://app1.local:9100/metrics"
},
{
name: "App2 metrics"
path: app2_metrics
labels: "{\"key3\": \"value3\", \"key4\": 4}"
url: "http://app2.local:9100/metrics"
},
{
name: "App3 metrics"
path: app3_metrics
labels: "{\"key5\": \"value5\", \"key6\": 6}"
url: "http://app3.local:9100/metrics"
}
]
}then the prometheus.yml scrape_config would target the three apps with:
- http://mymachine.local:8080/app1_metrics
- http://mymachine.local:8080/app2_metrics
- http://mymachine.local:8080/app3_metrics
If the endpoints were restricted with basic auth/bearer authentication, you could either include the basic-auth
credentials in the URL with: http://user:pass@hostname/metrics or they could be configured with basic_auth/
bearer_token in the scrape-config.
The prometheus.yml file would include:
scrape_configs:
- job_name: 'app1 metrics'
metrics_path: '/app1_metrics'
bearer_token: 'eyJ....hH9rloA'
static_configs:
- targets: [ 'mymachine.local:8080' ]
- job_name: 'app2 metrics'
metrics_path: '/app2_metrics'
basic_auth:
username: 'user'
password: 's3cr3t'
static_configs:
- targets: [ 'mymachine.local:8080' ]
- job_name: 'app3 metrics'
metrics_path: '/app3_metrics'
static_configs:
- targets: [ 'mymachine.local:8080' ]π **Docs site: ** Docker deployment guide covers compose files, bind mounts, and container configuration. Running on Kubernetes? See the Kubernetes guide.
The docker images support multiple architectures (amd64, arm64, s390x, ppc64le):
docker pull pambrose/prometheus-proxy:3.2.0
docker pull pambrose/prometheus-agent:3.2.0Start a proxy container with:
# Proxy with admin and metrics enabled
docker run --rm -p 8082:8082 -p 8092:8092 -p 50051:50051 -p 8080:8080 \
--env ADMIN_ENABLED=true \
--env METRICS_ENABLED=true \
--restart unless-stopped \
pambrose/prometheus-proxy:3.2.0Start an agent container with:
# Agent with remote config file
docker run --rm -p 8083:8083 -p 8093:8093 \
--env AGENT_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \
--restart unless-stopped \
pambrose/prometheus-agent:3.2.0Or use docker-compose: see etc/compose/proxy.yml for a working example.
Using the config file simple.conf, the proxy and the agent metrics would be available from the proxy on localhost at:
If you want to use a local config file with a docker container (instead of the above HTTP-served config file), use the
docker mount option. Assuming the config file prom-agent.conf
is in your current directory, run an agent container with:
# Agent with local config file
docker run --rm -p 8083:8083 -p 8093:8093 \
--mount type=bind,source="$(pwd)"/prom-agent.conf,target=/app/prom-agent.conf \
--env AGENT_CONFIG=prom-agent.conf \
pambrose/prometheus-agent:3.2.0Note: The WORKDIR of the proxy and agent images is /app, so make sure to use /app as the base directory in the
target for --mount options.
If you are running a JVM-based program, you can run with the agent embedded directly in your app and not use an external agent. This approach eliminates the need for a separate agent process when your application already runs on the JVM.
π **Docs site: ** Embedded Agent guide for the full API surface and lifecycle details.
// Start embedded agent
EmbeddedAgentInfo agentInfo = startAsyncAgent("configFile.conf", true);
// Your application code runs here
// The agent runs in the background and does not block your application
// Shutdown the agent when the application terminates
agentInfo.shutdown();If the configuration cannot be loaded (a parse error, an unreachable config URL, or a missing file), the embedded
startAsyncAgent / startSyncAgent entry points throw io.prometheus.common.ConfigLoadException for your application
to catch, rather than terminating the host JVM via exitProcess. Stand-alone agents (run from the CLI) still exit on a
missing or unreadable config.
Enable Prometheus service discovery support:
java -jar prometheus-proxy.jar \
--sd_enabled \
--sd_path discovery \
--sd_target_prefix http://proxy-host:8080/Access discovery endpoint at: http://proxy-host:8080/discovery
π **Docs site: ** Service Discovery guide explains the discovery payload format and the Prometheus
http_sd_configswiring.
Configure concurrent scraping:
java -jar prometheus-agent.jar \
--max_concurrent_clients 5 \
--client_timeout_secs 30 \
--config myconfig.confπ **Docs site: ** Monitoring guide for the full metrics reference and admin endpoints, plus Grafana & Alerting for ready-to-import dashboards and alert rules.
Both proxy and agent expose their own metrics:
- Proxy metrics:
http://proxy-host:8082/proxy_metrics - Agent metrics:
http://agent-host:8083/agent_metrics - Admin endpoints:
http://host:admin-port/ping,/healthcheck,/version
π **Docs site: ** Configuration overview and the CLI Reference document every option and environment variable.
The proxy and agent use the Typesafe Config library. Configuration values are evaluated in order: CLI options β environment variables β config file values.
Typesafe Config highlights include:
- support for files in three formats: Java properties, JSON, and a human-friendly JSON superset (HOCON)
- config files can be files or urls
- config values can come from CLI options, environment variables, Java system properties, and/or config files.
- config files can reference environment variables
π Complete configuration reference: CLI Options & Environment Variables Reference
| Component | Option | Env Var | Description |
|---|---|---|---|
| Both | --config, -c |
PROXY_CONFIG / AGENT_CONFIG |
Path or URL to config file |
| Both | --admin, -r |
ADMIN_ENABLED |
Enable admin/health-check endpoints |
| Both | --metrics, -e |
METRICS_ENABLED |
Enable internal metrics collection |
| Both | --agent_token |
AGENT_TOKEN |
Pre-shared agent auth token; both sides must match (Default: disabled) |
| Proxy | --port, -p |
PROXY_PORT |
Port for Prometheus to scrape (Default: 8080) |
| Proxy | --agent_port, -a |
AGENT_PORT |
Port for Agents to connect via gRPC (Default: 50051) |
| Agent | --proxy, -p |
PROXY_HOSTNAME |
Hostname/IP of the Proxy |
- Formats: Supports HOCON (
.conf), JSON (.json), and Java Properties (.properties). - Logging: Customize with
-Dlogback.configurationFile=/path/to/logback.xml. - Dynamic Props: Use
-Dproperty.name=valuefor any configuration key. - Keepalives: See the gRPC keepalive guide for tuning details.
π **Docs site: ** Example Configs guide describes each ready-to-run config under
examples/.
- π’ Enterprise environments - Scrape metrics across firewall boundaries
- βοΈ Multi-cloud deployments - Bridge different network segments
- π Secure environments - Monitor internal services without opening inbound ports
- π Federation - Scrape existing Prometheus instances via
/federateendpoint - π Kubernetes - Monitor services across clusters or namespaces
| Use Case | Configuration |
|---|---|
| Basic setup | examples/simple.conf |
| Multiple apps | examples/myapps.conf |
| TLS (no mutual auth) | examples/tls-no-mutual-auth.conf |
| TLS (with mutual auth) | examples/tls-with-mutual-auth.conf |
| Prometheus federation | examples/federate.conf |
| Nginx reverse proxy | nginx/nginx-proxy.conf |
π **Docs site: ** Advanced Topics guide covers the Nginx reverse-proxy setup, federation, and other advanced configurations.
Scrape an existing Prometheus instance via the /federate endpoint:
agent.pathConfigs: [{
name: "Federated Prometheus"
path: "federated_metrics"
url: "http://prometheus-server:9090/federate?match[]={__name__=~\"job:.*\"}"
}]This leverages the existing service discovery features already built into Prometheus.
Another service discovery example config can be found in federate.conf.
To use with Nginx as a reverse proxy, disable the transport filter on both Proxy and Agent:
java -jar prometheus-proxy.jar --tf_disabled
java -jar prometheus-agent.jar --tf_disabled --config myconfig.confAn example nginx conf file is here, and an example agent/proxy conf file is here
transportFilterDisabled, agent disconnections aren't immediately detected. Agent contexts on the
proxy
are removed after inactivity timeout (default: 1 minute, controlled by proxy.internal.maxAgentInactivitySecs).
gRPC Reflection is enabled by default for debugging and tooling.
Test with grpcurl:
# List available services
grpcurl -plaintext localhost:50051 list
# Describe a service
grpcurl -plaintext localhost:50051 describe io.prometheus.ProxyServiceIf you use grpcurl -plaintext option, make sure that you run the proxy in plaintext
mode, i.e., do not define any TLS properties.
Disable reflection: Use --ref_disabled, REFLECTION_DISABLED, or proxy.reflectionDisabled=true.
50051). A client such as grpcurl can enumerate every service, method, and message type without
credentials, making it trivial for an attacker who reaches the port to discover and craft calls (for example, to attempt
agent or path registration). The optional agent token does not protect reflection β the
token interceptor guards only the ProxyService RPCs, not the separate reflection service. Leave reflection on only for
local debugging on a trusted network; disable it in production and anywhere the agent port is reachable beyond trusted
agents. Disabling reflection only hides the API shape β it is not a substitute for authentication, so still pair the agent
port with the pre-shared agent token, mutual TLS, and/or network segmentation.
π **Docs site: ** Security overview and the TLS Setup guide cover agent authentication, mutual TLS, and certificate management in depth.
Agents connect to a proxy using gRPC. gRPC supports TLS with or without mutual authentication. The necessary certificate and key file paths can be specified via CLI args, environment variables, and configuration file settings.
The gRPC docs describe how to set up TLS. The repo includes the certificates and keys necessary to test TLS support.
Running TLS without mutual authentication requires these settings:
certChainFilePathandprivateKeyFilePathon the proxytrustCertCollectionFilePathon the agent
Running TLS with mutual authentication requires these settings:
certChainFilePath,privateKeyFilePathandtrustCertCollectionFilePathon the proxycertChainFilePath,privateKeyFilePathandtrustCertCollectionFilePathon the agent
Run a proxy and an agent with TLS (with mutual authentication) with:
# Proxy with TLS
java -jar prometheus-proxy.jar \
--cert /path/to/server.crt \
--key /path/to/server.key \
--trust /path/to/ca.crt
# Agent with TLS
java -jar prometheus-agent.jar \
--config myconfig.conf \
--trust /path/to/ca.crt \
--cert /path/to/client.crt \
--key /path/to/client.keyRun a proxy and an agent with TLS (no mutual authentication) using the included testing certs and keys with:
java -jar prometheus-proxy.jar --config examples/tls-no-mutual-auth.conf
java -jar prometheus-agent.jar --config examples/tls-no-mutual-auth.confRun a proxy and an agent docker container with TLS (no mutual authentication) using the included testing certs and keys with:
docker run --rm -p 8082:8082 -p 8092:8092 -p 50440:50440 -p 8080:8080 \
--mount type=bind,source="$(pwd)"/testing/certs,target=/app/testing/certs \
--mount type=bind,source="$(pwd)"/examples/tls-no-mutual-auth.conf,target=/app/tls-no-mutual-auth.conf \
--env PROXY_CONFIG=tls-no-mutual-auth.conf \
--env ADMIN_ENABLED=true \
--env METRICS_ENABLED=true \
pambrose/prometheus-proxy:3.2.0
docker run --rm -p 8083:8083 -p 8093:8093 \
--mount type=bind,source="$(pwd)"/testing/certs,target=/app/testing/certs \
--mount type=bind,source="$(pwd)"/examples/tls-no-mutual-auth.conf,target=/app/tls-no-mutual-auth.conf \
--env AGENT_CONFIG=tls-no-mutual-auth.conf \
--env PROXY_HOSTNAME=mymachine.lan:50440 \
--name docker-agent \
pambrose/prometheus-agent:3.2.0Note: The WORKDIR of the proxy and agent images is /app, so make sure to use /app as the base directory in the
target for --mount options.
The proxy accepts agent gRPC connections with no application-level authentication by default β any process that can reach
the agent port (default 50051) can register as an agent. Set a shared pre-shared token so the proxy rejects agents
that do not present it:
- Proxy:
--agent_token,AGENT_TOKEN, orproxy.agentToken - Agent:
--agent_token,AGENT_TOKEN, oragent.agentToken
Both sides must use the same value. When set, the agent attaches the token as a gRPC metadata header on every call and
the proxy rejects any call with a missing or mismatched token (UNAUTHENTICATED). When the token is empty (the default),
the open behavior is preserved and the proxy logs a startup warning β unless mutual TLS is configured, which already
authenticates agents.
# Proxy requiring a token
java -jar prometheus-proxy.jar --agent_token "$AGENT_TOKEN"
# Agent presenting the token
java -jar prometheus-agent.jar --config myconfig.conf --agent_token "$AGENT_TOKEN"The token is a lightweight, app-level control. For production, prefer mutual TLS (and/or restrict the agent port to trusted networks); the token complements, but does not replace, those controls. The value is never written to logs.
When Prometheus scrape configurations include basic_auth or bearer_token, the proxy forwards the
Authorization header to the agent over the gRPC channel. If TLS is not configured, these credentials
are transmitted in plaintext and could be intercepted on the network between the proxy and agent.
Enable TLS when forwarding auth headers:
# Proxy with TLS to protect forwarded credentials
java -jar prometheus-proxy.jar \
--cert /path/to/server.crt \
--key /path/to/server.key
# Agent with TLS
java -jar prometheus-agent.jar \
--config myconfig.conf \
--trust /path/to/ca.crtThe proxy logs a warning on the first request that includes an Authorization header when TLS is not enabled.
For HTTPS scrape targets signed by a custom or private CA (e.g. an internal corporate CA), point the agent at a trust store that contains that CA so certificates are still validated:
java -jar prometheus-agent.jar \
--https_truststore /etc/agent/truststore.jks \
--https_truststore_password changeit \
--config myconfig.confThis is also configurable via the HTTPS_TRUST_STORE_PATH / HTTPS_TRUST_STORE_PASSWORD environment vars or
the agent.http.trustStorePath / agent.http.trustStorePassword properties. An empty path uses the JDK
default trust store. The trust store is process-wide β it applies to every HTTPS target the agent scrapes.
As a last resort you can disable certificate verification entirely with the TRUST_ALL_X509_CERTIFICATES
environment var, the --trust_all_x509 CLI option, or the agent.http.enableTrustAllX509Certificates
property:
java -jar prometheus-agent.jar --trust_all_x509 --config myconfig.conf--trust_all_x509 in development/testing environments. Its scope is
process-global and all-or-nothing: it disables certificate validation for every HTTPS target the agent
scrapes, and it takes precedence over --https_truststore. Prefer a custom trust store whenever possible.
π **Docs site: ** Troubleshooting guide is a symptom-driven reference covering more failure modes and fixes.
Agent can't connect to proxy:
- Verify proxy hostname and port
- Check firewall rules (agent needs outbound access to proxy port)
- Ensure proxy is running and listening
TLS connection failures:
- Verify certificate paths and file permissions
- Check certificate validity and chain
- Ensure clock synchronization between proxy and agent
Metrics not appearing:
- Verify agent path configuration matches Prometheus scrape paths
- Check agent logs for scraping errors
- Confirm target endpoints are accessible from agent
Performance issues:
- Increase
max_concurrent_clientsfor high-throughput scenarios - Tune HTTP client cache settings
- Consider running multiple agents for load distribution
Full documentation is available at the Prometheus Proxy Documentation site.
KDoc API documentation is published at pambrose.github.io/prometheus-proxy/kdocs.
Note: Running on Kubernetes? See the
Kubernetes deployment guide for ready-to-use proxy and
agent manifests, standalone and sidecar agent patterns, gRPC exposure for remote agents, and Prometheus Operator
(ServiceMonitor) integration.
Note: Deploying to production? The Running in Production guide pulls the security, reliability, and tuning settings into a single operational checklist.
The library is published to Maven Central under com.pambrose:prometheus-proxy:
// build.gradle.kts
repositories {
mavenCentral()
}
dependencies {
implementation("com.pambrose:prometheus-proxy:3.2.0")
}This project is licensed under the Apache License 2.0 - see License.txt for details.
