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.
Version 4.0.1 (released 2026-07-31) is a dashboard patch release: agents are now identified by name rather than internal id in the path layout, 29 WCAG AA contrast failures are fixed (measured floor 4.59:1), and the page is usable with a screen reader — see the changelog for the full list. Nothing in it changes configuration, metrics, or the wire protocol.
Version 4.0.0 (released 2026-07-25) added five features focused on running the proxy as production infrastructure:
metric_relabel_configs can't reach. Per-path allow/deny regexes with Prometheus-compatible anchoring; whole families are kept or dropped atomically.📖 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 the agent behind 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 the proxy to the actual /metrics scraping 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
🔗 Agent - Runs inside the firewall with monitored services
📖 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
agentJar and proxyJar are dedicated ShadowJar tasks; the default shadowJar task 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:4.0.1
# Start agent
docker run --rm \
--env AGENT_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \
pambrose/prometheus-agent:4.0.1
📖 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:
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:4.0.1
docker pull pambrose/prometheus-agent:4.0.1
Start 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:4.0.1
Start 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:4.0.1
Or 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:4.0.1
Note: 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.
By default the agent's scrape targets come from the static agent.pathConfigs list, read once at startup — adding or removing a target means editing the config and restarting the agent. Enable dynamic discovery to have the agent reconcile its registered paths against a watched file at runtime, with no restart:
agent {
discovery {
enabled = true
file.path = "/etc/prometheus-proxy/targets.conf" // HOCON or JSON list of paths
reconcileIntervalSecs = 30 // Poll and full-resync interval
}
}
The discovery file is a paths list of the same { name, path, url, labels } entries as pathConfigs:
paths = [
{ name = "app1", path = "app1_metrics", url = "http://app1:9090/metrics" }
{ name = "app2", path = "app2_metrics", url = "http://app2:9090/metrics" }
]
Every reconcileIntervalSecs the agent reads the file and registers new paths, unregisters ones no longer listed, and re-registers a path whose URL or labels changed — without disturbing the paths that did not change. Behavior worth knowing:
agent.pathConfigs (which is never touched); on a path collision the static entry wins. Leave pathConfigs empty to run discovery-only.discovery.file.path points at a list, so there is no CLI/env equivalent (though enabled, the path, and the interval are scalars you can also set via -D).Because the file is a plain HOCON/JSON list, it can be generated — by a Kubernetes ConfigMap, an Ansible template, or a cron job querying a service registry — so your automation only needs permission to write a file, not to restart processes. A fully annotated example ships as examples/discovery-targets.conf.
This is distinct from the Prometheus Service Discovery above: that exposes a discovery endpoint so Prometheus can find proxied targets, whereas this lets the agent pick up target changes behind the firewall without a restart.
By default the agent forwards every metric a target exposes, so chatty or high-cardinality endpoints pay full bandwidth across exactly the network boundary this product exists to bridge. An optional per-path filter drops whole metric families at the agent, before the payload is gzipped and chunked, cutting WAN bandwidth and downstream Prometheus cardinality at the source:
agent {
pathConfigs: [
{ name: "app1", path: "app1_metrics", url: "http://app1:9090/metrics" }
]
filters: [
{
path: "app1_metrics" // The pathConfigs (or discovered) path this applies to
metricNameAllow: [] // Fully-anchored regexes; empty allows every family
metricNameDeny: [ "go_gc_.*", "go_memstats_.*" ]
}
]
}
Both list fields are required on every element — write metricNameAllow: [] for a deny-only filter. The default is filters: [], so an existing config with no filters key loads unchanged. Behavior worth knowing:
relabel_config semantics: "go_" matches nothing, "go_.*" is required to match go_goroutines. metricNameDeny is applied after metricNameAllow, so deny wins on overlap.# HELP/# TYPE line opens a family and the verdict is computed once, so a histogram's _bucket, _sum, and _count series are always kept or dropped together with their metadata lines — a filter can never deliver a histogram with some of its series missing.Content-Type (protobuf, an HTML error body) or a body that is not valid UTF-8 passes through unfiltered and byte-exact. The worst case is bandwidth not saved, never a corrupted payload.pathConfigs entries and paths added by Dynamic Target Discovery.agent_filter_lines_dropped and agent_filter_bytes_saved, labeled by launch_id and path, report what each filter is actually removing.filters is a list, so there is no CLI/env equivalent.An invalid regex fails agent startup rather than surfacing later on a scrape. Not implemented: dropLabels, metric renaming/relabeling, and an agent-global filter — every filter is per-path.
A single proxy is a single point of failure for every target behind every agent — losing it (or just restarting it to change config) blinds Prometheus to everything on the far side of the firewall at once. For redundancy, give the agent an ordered list of proxy endpoints instead of one hostname:
agent {
proxy {
endpoints = [ "proxy-a.example.com:50051", "proxy-b.example.com:50051" ]
}
}
Or comma-separated on the command line / environment (either replaces endpoints entirely):
java -jar prometheus-agent.jar --proxy proxy-a.example.com:50051,proxy-b.example.com:50051
# or: PROXY_HOSTNAME=proxy-a.example.com:50051,proxy-b.example.com:50051
The agent connects to the first endpoint that answers. A failed connect moves it to the next; a connection that came up and then dropped sends it back to the head of the list, so a recovered primary is picked up on the next reconnect with no health prober and no manual step. Only one connection is active at a time, and a single value behaves exactly as before — existing configs need no change.
Two things to know before deploying a pair:
static_config, not http_sd_config. A standby proxy has no agents, so its service-discovery endpoint returns an empty list — which Prometheus treats as "these targets no longer exist" and silently deletes them: no up=0, no alert. With static_config the standby 404s, Prometheus records up=0, and your alerting works unchanged.overrideAuthority), so proxies with different CAs or certificate SANs fail with an opaque handshake error.📖 Docs site: High availability for the full Prometheus-side setup, and Running an HA pair for how the dashboard shows failed-over agents.
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.
The proxy ships a read-only, live-updating dashboard that answers the question this system raises most often — why isn't this target scraping? — without log-diving on two machines. It shows every connected agent, every registered path (including paths whose agent has gone, which would otherwise vanish exactly when you go looking), and how recent scrapes went:
java -jar prometheus-proxy.jar --dashboard # or DASHBOARD_ENABLED=true / proxy.dashboard.enabled
Then open http://proxy-host:8094/dashboard. Two layouts share the page: Agents (drill into one agent) and Paths (one row per target). Both name the agent serving a path, so the two layouts can be read against each other. It runs on its own port — not the admin port — so it can be firewalled without cutting off Kubernetes health probes, and it loads no CDN assets, so it works in airgapped deployments. Like the admin and metrics endpoints it has no authentication or TLS: keep the port internal.
Accessibility: text and UI colors clear the WCAG AA contrast thresholds with a measured floor of 4.59:1, the path table carries proper header semantics, and connection loss and recovery are announced to screen readers rather than signalled by colour alone.
📖 Docs site: Dashboard guide for both layouts, the status-bar gauges, and HA-pair behavior.
Both proxy and agent expose their own metrics:
http://proxy-host:8082/proxy_metricshttp://agent-host:8083/agent_metricshttp://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:
📖 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). For per-agent identities see proxy.auth |
| 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) |
| Proxy | --dashboard |
DASHBOARD_ENABLED |
Enable the read-only operational dashboard (Default: disabled) |
| Proxy | --dashboard_port |
DASHBOARD_PORT |
Port for the operational dashboard (Default: 8094) |
| Agent | --proxy, -p |
PROXY_HOSTNAME |
Hostname/IP of the Proxy. Accepts a comma-separated failover list (e.g. proxy-a:50051,proxy-b:50051); see agent.proxy.endpoints |
.conf), JSON (.json), and Java Properties (.properties).-Dlogback.configurationFile=/path/to/logback.xml.-Dproperty.name=value for any scalar configuration key (parsed as Java properties, so list/object values like proxy.auth must be set in a config file).proxy.auth (per-agent identities with path authorization) is a list of objects, so it is config-file-only — see Per-Agent Identities and Path Authorization.📖 Docs site: Example Configs guide describes each ready-to-run config under
examples/.
/federate endpoint| Use Case | Configuration |
|---|---|
| Basic setup | examples/simple.conf |
| Multiple apps | examples/myapps.conf |
| Metric filtering | examples/agent-filters.conf |
| Dynamic discovery | examples/discovery-targets.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.conf
An example nginx conf file is here, and an example agent/proxy conf file is here
⚠️ Note: With 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.ProxyService
If 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.
⚠️ Security Note: Reflection is unauthenticated and exposes the full gRPC API surface to anyone who can reach the agent port (default 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:
certChainFilePath and privateKeyFilePath on the proxytrustCertCollectionFilePath on the agentRunning TLS with mutual authentication requires these settings:
certChainFilePath, privateKeyFilePath and trustCertCollectionFilePath on the proxycertChainFilePath, privateKeyFilePath and trustCertCollectionFilePath on the agentRun 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.key
Run 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.conf
Run 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:4.0.1
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:4.0.1
Note: 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:
--agent_token, AGENT_TOKEN, or proxy.agentToken--agent_token, AGENT_TOKEN, or agent.agentTokenBoth 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.
A single shared agentToken authenticates agents but does not distinguish between them: any agent holding the token can register any path, including one already served by another agent. To scope agents, define named identities under proxy.auth, each with its own token and a list of allowed path glob patterns:
proxy {
auth = [
{ name = team-a, token = "team-a-token", paths = ["team_a_*"] }
{ name = team-b, token = "team-b-token", paths = ["team_b_*"] }
{ name = infra, token = "infra-token", paths = [] } # empty paths = may register any path
]
}
Each agent presents its identity's token the usual way (--agent_token, AGENT_TOKEN, or agent.agentToken); no agent-side change is needed. The proxy resolves the token to an identity and enforces, on every registerPath, that the requested path matches one of the identity's paths patterns. Patterns are single-segment globs where * matches any run of characters and ? matches one (e.g. team_a_*); an empty paths list authorizes every path. An unknown token is rejected with UNAUTHENTICATED; a path outside the identity's patterns fails registration with a "not authorized" reason. Because authorization is per-identity-per-path, consolidated mode (multiple agents serving one path) still works as long as each agent's identity permits the shared path.
proxy.auth is a list of objects, so it is set in the config file only — there is no equivalent CLI flag or environment variable. Identity names must be unique and tokens must be non-empty; the proxy fails fast at startup otherwise.
Setting proxy.auth does not disable a legacy proxy.agentToken. When both are present, the shared token is honored as an additional allow-all identity (and the proxy logs a warning that it is active), so you can adopt per-agent identities incrementally:
proxy.auth entry per agent while leaving proxy.agentToken in place — existing agents keep connecting with the shared token.proxy.agentToken to close the shared allow-all path.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.crt
The 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.conf
This 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
⚠️ Security Note: Only use --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:
TLS connection failures:
Metrics not appearing:
Performance issues:
max_concurrent_clients for high-throughput scenariosFull 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:4.0.1")
}
This project is licensed under the Apache License 2.0 - see License.txt for details.