postgres_exporter: Prometheus metrics for PostgreSQL, and where it stops
A PostgreSQL metric exporter for Prometheus
At a glance
- What is it?
- The prometheus-community exporter turns PostgreSQL server state into Prometheus metrics on port 9187, with a multi-target probe endpoint for databases it cannot sit beside. Here is how the collectors, config file and Docker image fit together, and what the documentation leaves open.
- Who is it for?
- Adopt postgres_exporter when you already run Prometheus and need PostgreSQL server metrics without writing your own SQL-to-metric layer; the Docker image and the /probe endpoint cover both sidecar and SaaS-managed layouts. Do not adopt it if you need query-level latency histograms or per-statement tracing out of the box, or if you cannot grant a monitoring role read access to pg_stat_statements.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What postgres_exporter actually collects
PostgreSQL keeps a large amount of operational state inside system views: connection counts, lock waits, replication lag, WAL receiver status, autovacuum activity, table and index statistics. Prometheus cannot read those views. It scrapes HTTP endpoints that return text in the exposition format. postgres_exporter is the translation layer between the two: it connects to a PostgreSQL server with a DSN, runs SQL, and serves the results as metrics over HTTP.
The intended audience is narrow but deep. It is for teams that already run Prometheus and want PostgreSQL on the same dashboards and alert rules as the rest of their stack, rather than in a separate monitoring product. It is not a general PostgreSQL observability tool. It does not sample queries, it does not store history, and it does not alert. Everything it does happens at scrape time, on demand, with the results handed to Prometheus and then discarded by the exporter.
The collector set is where the real decision-making lives. The README lists roughly twenty collectors, each toggled by a --collector.<name> flag. Only some are on by default: database, locks, replication, replication_slots, settings, stat_activity, stat_archiver, stat_bgwriter, stat_database, stat_progress_vacuum, stat_replication and stat_user_tables. Others, including database_wraparound, long_running_transactions, postmaster, process_idle, stat_activity_autovacuum, stat_checkpointer, stat_statements and stat_wal_receiver, are disabled until you ask for them. That default split matters more than any single metric: a fresh install will not show you transaction wraparound risk or long-running transactions until you turn those collectors on.
How a scrape turns SQL into Prometheus text
The data flow is one-directional and stateless. Prometheus issues an HTTP GET to the exporter. The exporter opens or reuses a connection to PostgreSQL using the DSN built from DATA_SOURCE_URI, DATA_SOURCE_USER and DATA_SOURCE_PASS, or from an auth module in the config file. Each enabled collector runs its queries, converts rows into metric families, and the HTTP handler writes them out. Prometheus stores the samples. The exporter keeps nothing.
That statelessness is a design choice with visible consequences. Restarting the exporter loses no data, because there is none to lose. It also means every scrape re-runs the collector queries, so a collector that is expensive on a large catalog is expensive on every scrape interval, not once. The stat_statements collector is the clearest example: the README documents a --collector.stat_statements.limit defaulting to 100, a --collector.stat_statements.query_length defaulting to 120, and exclude_databases and exclude_users lists to keep the query set bounded. Those defaults exist because pg_stat_statements on a busy server can return a very large number of rows.
The multi-target pattern changes the shape of the deployment. Instead of one exporter per database, a single instance answers /probe?target=foo:5432 and connects to whatever DSN the caller names. The README is explicit that this is optional and intended for cases where installing the exporter as a sidecar is impossible, naming SaaS-managed services. To keep credentials out of the URL, auth_modules in the config file define named userpass modules, and Prometheus passes ?auth_module=foo instead. Only the userpass type is supported, so certificate-based or IAM-style authentication has to be handled another way.
The README's own Prometheus example contains a detail worth reading twice: the relabel_configs set __address__ to 127.0.0.1:9116, with a comment calling that the postgres exporter's real hostname:port. The Dockerfile exposes 9187, and the quick start curls localhost:9187/metrics. The two numbers in the same README do not agree, which is a good reason to confirm the port your own deployment binds rather than copying either value.
Installing postgres_exporter with Docker and taking a first scrape
The README leads with Docker, and that is the shortest path to a running instance. The first command starts a throwaway PostgreSQL server with a known password; the second starts the exporter pointed at it. Both use --net=host so the exporter can reach the database on localhost.
# Start an example database
docker run --net=host -it --rm -e POSTGRES_PASSWORD=password postgres
# Connect to it
docker run \
--net=host \
-e DATA_SOURCE_URI="localhost:5432/postgres?sslmode=disable" \
-e DATA_SOURCE_USER=postgres \
-e DATA_SOURCE_PASS=password \
quay.io/prometheuscommunity/postgres-exporterOnce the container is up, the README's verification step is a single curl against the metrics endpoint. You should see Prometheus exposition text, with metric names carrying the pg_ prefix and one line per label combination.
curl "http://localhost:9187/metrics"If you prefer to build from source rather than pull the image, the README gives a clone-and-make sequence. The result is a binary in the repository root that takes the same flags as the container.
git clone https://github.com/prometheus-community/postgres_exporter.git
cd postgres_exporter
make build
./postgres_exporter <flags>The exporter reads a configuration file by default. The README states that --config.file defaults to postgres_exporter.yml and that passing --config.file="" runs without one. That file is where auth_modules live, so a deployment using /probe with named modules needs the file present and readable by the process. The Dockerfile runs as USER nobody, which is worth remembering when you mount a config file into the container: file permissions that work for root may not work here.
Enabling a collector that is off by default is a flag change, not a config change. To get pg_stat_statements metrics, for example, the README documents --collector.stat_statements along with its companion settings for query length, row limit and database or user exclusions.
The collectors that are off by default, and why that is the real limitation
The most common complaint about an exporter like this is not that it fails, but that it is quiet about things you assumed it covered. Here the gap is documented but easy to miss. database_wraparound, which is the signal for transaction ID exhaustion, is disabled by default. So is long_running_transactions. So is stat_checkpointer. An operator who installs the exporter, points Prometheus at it, and builds a dashboard from whatever metrics appear will not see any of those series, and nothing in the metrics output announces their absence.
The stat_statements collector has a sharper constraint. It depends on pg_stat_statements being installed and populated on the server, which is a PostgreSQL extension, not something the exporter can create. The README documents exclusion lists and limits for the collector, which implies the authors expect it to be pointed at servers where the statement set is large. On a server where the extension is absent, enabling the flag produces no useful series rather than an error you would notice from the dashboard.
There is also a permission surface. The exporter needs a PostgreSQL role that can read the system views behind each enabled collector, and the README points to docs/ for database permissions rather than spelling them out inline. A role that works for the default collector set may not work for stat_statements or the replication collectors, and the failure mode is missing metrics rather than a failed scrape.
Finally, the exporter is the wrong tool if what you want is query-level latency distributions, per-request tracing, or log correlation. It exposes server state, not application behaviour. A team debugging a slow endpoint will not find the answer in pg_ metrics.
postgres_exporter compared with running your own SQL exporter
The obvious alternative is the generic SQL exporter approach: a tool that reads a YAML file of queries and column mappings and produces metrics from arbitrary SQL. The difference in approach is where the SQL lives. postgres_exporter ships its queries inside the Go binary, organized as collectors with flags controlling which run. A generic SQL exporter ships almost no SQL; you write every query yourself and maintain it as your PostgreSQL version changes.
That trade-off cuts both ways. With postgres_exporter you get working metrics immediately and the query set is maintained upstream, which is why the repository has a queries.yaml and a collector/ directory rather than asking users to bring their own. The cost is that you cannot easily add a metric the project does not expose without forking or extending it, and the set of collectors is fixed by what the maintainers have chosen to support. With a generic SQL exporter you can express any query you want, including application-specific ones, but you own the correctness of every query across PostgreSQL upgrades and you get no defaults at all.
A second alternative is to skip the exporter and scrape PostgreSQL through an existing agent that already runs on the host. That reduces the number of moving parts but couples database metrics to the host agent's release cycle. postgres_exporter's advantage in that comparison is the multi-target endpoint: one exporter process can serve many databases, which a per-host agent cannot do without being installed on every database host.
Maintenance, releases and the Apache-2.0 licence
The repository is not archived, and the last push was on 2026-09-23. Recent releases are v0.20.1 (2026-07-07), v0.20.0 (2026-06-29) and v0.19.1 (2026-02-25), so releases arrive on a scale of months rather than weeks. For an operator, that cadence is comfortable: the metric names are the interface, and they change rarely. The upgrade cost is mostly in the collector flags, which are additive, and in the Go toolchain if you build from source. The go.mod pins go 1.25.0, so a source build needs a toolchain at least that new.
CI tests against PostgreSQL 13 through 18 according to the README. If you run an older major version, the exporter may still work, but the tested surface does not include you, and the collectors that read newer system views are the ones most likely to behave differently.
The licence is Apache-2.0. That permits commercial use and modification, and it includes a patent grant. It also requires that you preserve the LICENSE and NOTICE files, which the Dockerfile copies into the image alongside the binary. If you redistribute a modified build, the NOTICE file is not optional. None of this is legal advice; check with your own counsel if you are bundling the exporter into a product.
Editorial conclusion
Adopt postgres_exporter when you already run Prometheus and need PostgreSQL server metrics without writing your own SQL-to-metric layer; the Docker image and the /probe endpoint cover both sidecar and SaaS-managed layouts. Do not adopt it if you need query-level latency histograms or per-statement tracing out of the box, or if you cannot grant a monitoring role read access to pg_stat_statements. Before rollout, verify three things: which collectors are enabled by default versus disabled (stat_statements, stat_checkpointer, postmaster, process_idle and others start off), whether your PostgreSQL version is in the CI-tested range of 13 through 18, and whether your config file path is actually being read, since --config.file defaults to postgres_exporter.yml and silently running without one changes what the /probe endpoint can authenticate.
Frequently asked questions
How can I use postgres_exporter with Prometheus?
Run the exporter with a DSN pointing at your PostgreSQL server, then add a scrape job in Prometheus targeting the exporter's metrics endpoint. The README's quick start uses Docker with DATA_SOURCE_URI, DATA_SOURCE_USER and DATA_SOURCE_PASS, and verifies with a curl against the metrics port.
How do I install postgres_exporter?
The README gives two routes: pull quay.io/prometheuscommunity/postgres-exporter and run it with the DATA_SOURCE_* environment variables set, or clone the repository and run make build followed by ./postgres_exporter with your flags. The docs/ directory covers the other installation methods.
How do I set up postgres_exporter?
Start it against a database, then confirm the metrics endpoint responds. For multiple targets, the README documents the /probe endpoint with a target parameter and optional auth_module, plus a scrape_configs example that relabels __address__ to the exporter's host and port.
What is postgres_exporter?
It is a Prometheus exporter for PostgreSQL server metrics, written in Go and published under Apache-2.0. It connects to PostgreSQL, runs collector queries, and serves the results as Prometheus metrics.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/prometheus-community-postgres-exporter)