Monitoring PostgreSQL with OpenTelemetry means collecting database metrics through the OpenTelemetry Collector's PostgreSQL receiver and exporting them over OTLP, so database health sits alongside application traces..
Monitoring PostgreSQL with OpenTelemetry means collecting database metrics through the OpenTelemetry Collector's PostgreSQL receiver and exporting them over OTLP, so database health sits alongside application traces.
PostgreSQL trouble is predictable if you watch the right things: connection usage against the configured maximum, transaction throughput, the buffer-cache hit ratio, lock contention and blocked queries, replication lag on any replicas, and disk usage. The two that cause most user-facing latency are slow or blocked queries and a cache hit ratio that falls because the working set no longer fits in shared buffers.
The highest-leverage view is the one that links a slow application request to the exact statement behind it, which is why database telemetry is most useful when the query the app ran is carried on the request's trace and the database's own metrics sit alongside it.
The OpenTelemetry Collector's postgresql receiver connects to the server as a monitoring user and reads the internal statistics views, emitting database and table-level metrics, backends, commits and rollbacks, block reads and cache hits, replication delay and database size, on an interval. For query-level detail, enable the slow-query log (log_min_duration_statement) and collect it with the filelog receiver, and enable pg_stat_statements so the worst statements are attributable.
The permission detail that catches people is the monitoring role: grant the receiver's user the pg_monitor role (or the specific stat privileges), or the statistics views return partial or empty data and the metrics look wrong. Application spans that carry the SQL statement as an attribute complete the picture by tying a query's latency to the request that issued it.
The signals that predict most PostgreSQL incidents:
| Signal | Source | What it tells you / watch for |
|---|---|---|
postgresql.backends | postgresql receiver | Active connections. Approaching max_connections is the road to 'too many clients' errors. |
postgresql.blocks_read vs cache hits | postgresql receiver | Compute the cache hit ratio; a falling ratio means the working set no longer fits in shared_buffers. |
postgresql.commits / rollbacks | postgresql receiver | Transaction throughput and a rollback rate that signals application or contention problems. |
postgresql.replication.data_delay | postgresql receiver | Replication lag on replicas; growing lag means replicas serve stale data. |
postgresql.deadlocks | postgresql receiver | Deadlocks per database; a rising count is a contention pattern to fix. |
postgresql.db_size | postgresql receiver | Database growth and, with disk metrics, how close you are to running out of space. |
A PostgreSQL dashboard should answer: are we running out of connections, is the cache still doing its job, and is anything blocked or lagging. Row one: backends against max_connections, and the cache hit ratio computed from block reads and hits. Row two: commits and rollbacks per second, deadlocks, and long-running or blocked query count. Row three: replication lag per replica and database size against disk capacity.
# connection saturation (fraction of max_connections in use)
postgresql.backends / postgresql.connection.max
# buffer cache hit ratio (below ~0.99 on OLTP is worth investigating)
cache_hits / (cache_hits + postgresql.blocks_read)
# replication lag per replica
postgresql.replication.data_delay by (replica)
Applications start receiving 'too many clients already' and latency spikes as requests wait for a connection. postgresql.backends sits at the ceiling. The cause is usually a missing or undersized connection pooler (PgBouncer) or a leak of unclosed connections; pool in front of Postgres rather than raising max_connections indefinitely.
Read latency rises and disk I/O climbs because queries are hitting disk instead of shared buffers. The hit ratio computed from block reads and hits falls below its normal high-nineties. Either the working set has outgrown shared_buffers, or a query is scanning far more data than it should, findable via pg_stat_statements.
Some queries hang while others proceed, because a long transaction holds a lock the others need. Blocked-query count and lock waits rise, and deadlocks may appear. Find the blocking transaction and the statement holding the lock; the usual root cause is a long-running write transaction or a missing index forcing broad locks.
Starting thresholds to tune:
| Condition | Signal | Meaning |
|---|---|---|
| backends / max_connections > 0.85 for 5m | connections | Running out of connections. |
| cache hit ratio < 0.95 for 15m | cache | Working set spilling to disk; latency rising. |
| replication data_delay > 30s for 5m | replication | Replicas serving increasingly stale data. |
| deadlocks increasing over 10m | contention | A contention pattern is developing. |
Ops Singularity's TelemetryOps ingests PostgreSQL telemetry over OpenTelemetry, and its DataOps and Sentinel capabilities resolve database incidents, a lock storm, a runaway query, replication lag, through governed Action Tickets. Explore TelemetryOps and DataOps.
Use the OpenTelemetry Collector's postgresql receiver to collect database metrics and the filelog receiver for logs, exported over OTLP, and correlate with application spans.
Connection usage against limits, cache hit ratio, replication lag, lock contention and long-running queries, transaction throughput, and disk usage.
Bring a real incident. We will show you Sentinel investigate, act and verify end to end, with every action reversible and audited.