Troubleshooting
Check the process, HTTP access, database connection, then the failing collector. pg_up 1 means the last scrape reached the main target and completed its fatal collectors. Other collectors or discovered databases can still have errors.
Initial Checks
| Observation | Layer to investigate |
|---|---|
| Unit exits immediately | Arguments, config path, telemetry path, listen address, file permissions |
Port answers but /up is 503 |
Target URL, network, authentication, pg_hba.conf, TLS, startup probe state |
/up is 200 but a metric family is absent |
Dynamic plan, version/role/tags/predicates, auto-discovery, disabled collector |
/metrics returns an error or Prometheus times out |
Slow query, scrape timeout, duplicate or invalid metric samples |
| Exporter is healthy but Prometheus target is down | Prometheus address, scheme, auth, TLS, firewall, telemetry path |
Process Will Not Start
no valid config path
The config search order is --config, PG_EXPORTER_CONFIG, ./pg_exporter.yml, /etc/pg_exporter.yml, then /etc/pg_exporter. Confirm the file exists and is readable by the runtime user:
--dry-run parses and explains the raw configuration without needing a live database. A config directory is non-recursive and loads only .yaml / .yml files in alphabetic order; a duplicate top-level branch name from a later file overrides the earlier definition.
Invalid telemetry path
Since v1.4.0, --web.telemetry-path must be a canonical literal path beginning with /. It cannot contain a query, fragment, Go ServeMux wildcard, or collide with built-in endpoints such as /up, /reload, or /version.
Use /metrics unless there is a concrete need to change it, and update the Prometheus metrics_path at the same time.
Address already in use
Stop the conflicting service or choose another --web.listen-address.
pg_up Is 0 or /up Returns 503
For the packaged service, add --log.level=debug to PG_EXPORTER_OPTS in /etc/default/pg_exporter, then restart the service. This preserves its user and connection settings:
For a foreground process, use the real target URL and supply the password through .pgpass or a secret file:
Check, in order:
- DNS and TCP reachability to the advertised host and port.
- Database name and login role.
pg_hba.confsource address, authentication method, and reload state.- Password source: URL,
.pgpass, orPG_EXPORTER_URL_FILE. .pgpassmode0600, hostname match, and the actual runtime user’s HOME.- TLS CA path, hostname, and
sslmode. - Connection limit on the monitoring role and server-wide connection exhaustion.
--connect-timeout: the default is 100 ms for connection and metadata probes. Increase it if normal network latency exceeds this budget.
After diagnosis, restore --log.level=info and restart the service.
The packaged service runs as prometheus; testing as your shell user can therefore succeed while the service fails.
Non-blocking startup is normal: while the target is unavailable, the HTTP server remains up and the background probe retries. Use --fail-fast only when the orchestrator should treat initial database unreachability as a process startup failure.
Metrics Are Missing
Scrape /metrics once to build the plan. /explain shows collectors selected for the main target. For a missing collector, compare its YAML with the target version, role, and tags; debug logs report why a branch was rejected. Check for:
- server version outside
[min_version, max_version); - primary/replica role mismatch;
- missing
extension:,schema:,dbname:, orusername:fact; - missing custom tag or matching
not:tag; - predicate query returning false;
skip: true;- database excluded from auto-discovery.
The include/exclude lists apply to additional discovered databases. The database in the connection URL is always monitored, even if its name appears in the default exclusion template0,template1,postgres. Predicates can skip a selected collector, and a successful query can return no rows; check /stat and the SQL result as well.
One Collector Fails
Use /stat for the main target. Use exporter self-metrics, labeled by datname and query, for all monitored databases:
This value counts errors since the last plan was built; a nonzero value can describe an earlier failure. Scrape again and check whether it increases. Read the log for the error text.
Common causes include:
- monitoring role lacks access to an optional view or function;
- extension/schema exists in a different database;
- custom SQL result columns do not match the YAML
metricslist; - a configured
LABELcolumn is absent (the exporter emits no metrics from that collector for this call); - the query exceeds its timeout;
- an extension or pre-release PostgreSQL view changed shape.
Run the collector SQL manually as the monitoring user in the same database. Preserve the result column names exactly, including zero-row result schemas.
Scrapes Are Slow or Time Out
/stat reports query durations in milliseconds for the main target. pg_exporter_query_scrape_duration reports seconds for each datname and query. Error counts are available in both; error text is in the logs.
Typical remedies:
- Optimize or narrow the SQL.
- Increase
ttlso expensive results are reused across scrapes. - Set a realistic per-query
timeoutbelow the Prometheus scrape timeout. - Disable optional high-cost collectors with
skip: true. - Reduce database auto-discovery scope with
include-database/exclude-database. - Control per-object metric cardinality on databases with thousands of tables or indexes.
Queries and discovered databases are scraped sequentially, so budget for their combined duration. Keep the full scrape below Prometheus scrape_timeout and scrape_interval. The HTTP server also has a 30-second write timeout.
Reload Fails
200: the new configuration passed validation and was loaded; the next scrape will rebuild the collection plans.500: the response contains the parse, schema, label-conflict, or config-path error. The old active query set remains in place.405: use GET or POST; POST is recommended.
Restart the service after changing the target URL, listen address, logging, namespace, discovery flags, or the path passed to --web.config.file. Exporter-toolkit reads the contents of that web configuration file dynamically; certificate or Basic Auth changes at the same path do not use /reload.
Collector YAML can also be reloaded with SIGHUP; Unix builds additionally accept SIGUSR1. A reload rebuilds plans on the next scrape and resets per-query statistics.
Docker and Package Pitfalls
| Symptom | Likely cause and fix |
|---|---|
| TLS verification fails in Docker | scratch image has no CA bundle; mount the CA and set sslrootcert |
.pgpass is ignored in packaged service |
Check the prometheus account home or PGPASSFILE; give the service user ownership and use mode 0600 |
| RPM automation cannot find v1.4.1 artifact | Artifact prefix changed from pg_exporter to pg-exporter |
| Config edits disappear after container replacement | Mount /etc/pg_exporter.yml from durable configuration |
| Container cannot reach host PostgreSQL | localhost means the container; use the database service DNS name or an explicit host route |
Evidence to Include in a Bug Report
pg_exporter --versionorcurl http://127.0.0.1:9630/versionoutput, and installation method- Operating system and architecture
- PostgreSQL or PgBouncer exact version
- Redacted target URL (retain scheme, host class, port, database, and
sslmode) - Relevant collector YAML and SQL, with secrets removed
- Debug log around the failure
/explainand/statentries for the collector- Whether the problem reproduces with the bundled v1.5.0 config
Remove passwords, private keys, customer identifiers, and sensitive SQL text, then submit the evidence to pgsty/pg_exporter Issues.