Home Database Database Automation & Observability: Prometheus, Grafana, pg_stat
Advanced 3 min · July 13, 2026

Database Automation & Observability: Prometheus, Grafana, pg_stat

Learn to automate database monitoring with Prometheus and Grafana using pg_stat views.

N
Naren Founder & Principal Engineer

20+ years shipping high-throughput database systems. Everything here is grounded in real deployments.

Follow
Production
production tested
July 13, 2026
last updated
2,165
articles · all by Naren
Before you start⏱ 20-25 min read
  • Basic knowledge of SQL and PostgreSQL
  • Familiarity with Linux command line
  • Understanding of monitoring concepts (metrics, alerts)
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Use Prometheus to scrape PostgreSQL metrics via postgres_exporter.
  • Grafana dashboards visualize pg_stat_* views for real-time insights.
  • Automate alerts for slow queries, replication lag, and connection saturation.
  • Production debugging: correlate pg_stat_activity with Prometheus metrics.
  • Common mistakes: missing index on pg_stat_statements, over-scraping causing overhead.
✦ Definition~90s read
What is Database Automation and Observability?

Database automation and observability is the practice of using tools like Prometheus, Grafana, and PostgreSQL's pg_stat views to automatically monitor, visualize, and alert on database performance and health.

Imagine your database is a car engine.
Plain-English First

Imagine your database is a car engine. pg_stat views are the dashboard gauges (RPM, temperature). Prometheus is a mechanic that reads those gauges every few seconds and logs them. Grafana is the display screen that shows you a graph of engine temperature over time. Automation means setting up alerts so the mechanic calls you when the engine overheats, before it breaks down.

In modern production environments, databases are the heart of your application. A single slow query or connection spike can cascade into a full outage. Yet many developers rely on ad-hoc queries to check database health. This tutorial introduces a robust observability stack: Prometheus for metric collection, Grafana for visualization, and PostgreSQL's pg_stat views as the data source. You'll learn to automate monitoring, detect anomalies, and debug production issues with confidence. We'll cover setting up postgres_exporter, building dashboards for key metrics like query performance, replication lag, and connection pools, and writing alerts that prevent incidents. By the end, you'll have a production-ready monitoring system that turns raw pg_stat data into actionable insights.

1. Understanding pg_stat Views

PostgreSQL provides a rich set of system views under the pg_stat schema that expose internal performance metrics. These views are the foundation for any monitoring solution. Key views include: - pg_stat_activity: Shows current server processes, including queries, state, and wait events. - pg_stat_database: Per-database statistics like transactions, tuples fetched, and cache hit ratio. - pg_stat_statements: Requires extension; tracks execution statistics of SQL statements (calls, total time, rows). - pg_stat_replication: Replication status for streaming replicas. - pg_stat_bgwriter: Background writer performance (checkpoints, buffers).

To enable pg_stat_statements, run: CREATE EXTENSION IF NOT EXISTS pg_stat_statements; and add 'pg_stat_statements' to shared_preload_libraries in postgresql.conf, then restart. These views are updated in real-time and provide granular data for observability.

query.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
-- Check top 5 queries by total execution time
SELECT query, calls, total_time, rows
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 5;

-- Current active queries
SELECT pid, state, query_start, query
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY query_start;
Output
query | calls | total_time | rows
---------------------------+-------+------------+------
SELECT * FROM orders ... | 1000 | 5000.123 | 5000
UPDATE products SET ... | 500 | 3000.456 | 1000
...
🔥pg_stat_statements Overhead
📊 Production Insight
In production, pg_stat_statements can accumulate millions of entries. Set pg_stat_statements.max to a reasonable value (e.g., 10000) and track top queries by total_time or mean_time.
🎯 Key Takeaway
pg_stat views are the raw data source for all PostgreSQL monitoring; pg_stat_statements is essential for query performance analysis.

2. Setting Up Prometheus for PostgreSQL

Prometheus is a time-series database that scrapes metrics from exporters. For PostgreSQL, we use the postgres_exporter (maintained by the Prometheus community). Installation steps: 1. Download the postgres_exporter binary from GitHub releases. 2. Create a monitoring user in PostgreSQL: CREATE USER pg_monitor WITH PASSWORD 'secret' INHERIT; GRANT pg_monitor TO pg_monitor; (or use a dedicated role with SELECT on pg_stat_* views). 3. Set environment variables: DATA_SOURCE_NAME="postgresql://pg_monitor:secret@localhost:5432/postgres?sslmode=disable" 4. Run the exporter: ./postgres_exporter --web.listen-address=:9187

The exporter exposes metrics at /metrics. Configure Prometheus to scrape this endpoint. Example prometheus.yml: scrape_configs: - job_name: 'postgresql' static_configs: - targets: ['localhost:9187']

Prometheus will collect metrics like pg_stat_database_tup_fetched, pg_stat_activity_count, and pg_stat_statements_total_time.

prometheus.ymlBASH
1
2
3
4
5
6
7
8
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'postgresql'
    static_configs:
      - targets: ['localhost:9187']
    metrics_path: /metrics
⚠ Security Considerations
📊 Production Insight
Set scrape_interval to 15-30 seconds to balance granularity and overhead. Too frequent scraping can impact database performance.
🎯 Key Takeaway
postgres_exporter translates pg_stat views into Prometheus metrics; configure Prometheus to scrape it at regular intervals.

3. Building Grafana Dashboards

Grafana connects to Prometheus as a data source and visualizes metrics. Install Grafana, add Prometheus data source (URL: http://localhost:9090). Then create dashboards. Essential panels: - Database Connections: Use query pg_stat_activity_count{datname="yourdb"} to show total connections over time. - Query Performance: pg_stat_statements_total_time{query!=""} top 5 queries. - Cache Hit Ratio: (pg_stat_database_blks_hit / (pg_stat_database_blks_hit + pg_stat_database_blks_read)) * 100. - Replication Lag: pg_stat_replication_lag.

You can import pre-built dashboards from Grafana.com (e.g., ID 9628 for PostgreSQL). Customize alerts: e.g., alert when connections > 80% of max_connections. Use Grafana's alerting rules or Prometheus Alertmanager.

grafana_query.sqlSQL
1
2
3
4
5
6
7
8
9
-- Example PromQL queries for Grafana
-- Connections by state
sum by (state) (pg_stat_activity_count{datname="mydb"})

-- Top 5 queries by total time
topk(5, pg_stat_statements_total_time{datname="mydb"})

-- Cache hit ratio
(avg by (datname) (pg_stat_database_blks_hit{datname="mydb"}) / (avg by (datname) (pg_stat_database_blks_hit{datname="mydb"}) + avg by (datname) (pg_stat_database_blks_read{datname="mydb"}))) * 100
💡Dashboard Organization
📊 Production Insight
Set up a 'Database Overview' dashboard with key metrics: connections, cache hit ratio, active queries, and replication lag. Pin it to your team's monitoring screen.
🎯 Key Takeaway
Grafana turns Prometheus metrics into visual dashboards; use PromQL to aggregate and filter pg_stat metrics.

4. Automating Alerts with Prometheus Alertmanager

Alertmanager handles alerts from Prometheus. Define alert rules in Prometheus configuration. Example rule for high connections:

groups: - name: postgresql_alerts rules: - alert: HighConnections expr: pg_stat_activity_count{datname="mydb"} > 80 for: 5m labels: severity: warning annotations: summary: "High database connections on {{ $labels.instance }}" description: "Connections are at {{ $value }}"

Other useful alerts
  • ReplicationLag: pg_stat_replication_lag > 10 (for 5m)
  • SlowQueries: rate(pg_stat_statements_total_time[5m]) > 1000 (for 5m)
  • CacheHitRatio: (pg_stat_database_blks_hit / (pg_stat_database_blks_hit + pg_stat_database_blks_read)) < 0.95

Configure Alertmanager to send notifications via email, Slack, PagerDuty, etc.

alert_rules.ymlBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
groups:
  - name: postgresql
    rules:
      - alert: HighConnections
        expr: pg_stat_activity_count{datname="mydb"} > 80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High connections on {{ $labels.instance }}"
      - alert: ReplicationLag
        expr: pg_stat_replication_lag > 10
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Replication lag on {{ $labels.instance }}"
🔥Alert Fatigue
📊 Production Insight
Start with a few critical alerts (connections, replication lag, cache hit ratio) and gradually add more. Test alerts in staging.
🎯 Key Takeaway
Alertmanager automates incident response; define rules based on pg_stat metrics and route alerts to the right channels.

5. Correlating Metrics with Application Performance

Observability is not just about database metrics; it's about connecting them to application behavior. Use Grafana's annotations to mark deployments or incidents. For example, when application response time spikes, check if database connections or slow queries also spiked. You can also instrument your application to emit custom metrics (e.g., request duration) and correlate with pg_stat_activity.

Example: If your app shows increased latency, query pg_stat_activity for queries running longer than 1 second. Use Prometheus to track query duration percentiles via pg_stat_statements. Create a dashboard that overlays application latency with database query time.

correlation.sqlSQL
1
2
3
4
5
6
-- Find queries running longer than 1 second
SELECT pid, now() - query_start AS duration, query
FROM pg_stat_activity
WHERE state = 'active'
  AND now() - query_start > interval '1 second'
ORDER BY duration DESC;
Output
pid | duration | query
-----+----------+----------------------------------------
123 | 00:02:15 | SELECT * FROM large_table WHERE ...
456 | 00:01:30 | UPDATE orders SET status = ...
💡Use Labels for Granularity
📊 Production Insight
Set up a 'Correlation Dashboard' that shows application latency, database connections, and top queries side by side. Use Grafana's template variables to filter by service.
🎯 Key Takeaway
Correlate database metrics with application performance to identify root causes faster.

6. Advanced: Automating Remediation with pg_stat

Beyond monitoring, you can automate responses to common issues. For example, a cron job can kill idle transactions that block other queries. Use pg_stat_activity to detect and terminate them. Example script:

#!/bin/bash psql -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle in transaction' AND state_change < now() - interval '30 minutes';"

Similarly, you can auto-scale connection pools or restart replicas if lag exceeds threshold. Integrate with orchestration tools like Kubernetes or Ansible. However, be cautious: automated actions can cause unintended side effects. Always log actions and have a manual override.

kill_idle.shBASH
1
2
3
4
5
6
7
8
9
#!/bin/bash
# Kill idle transactions older than 30 minutes
psql -U pg_monitor -d postgres -c "
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle in transaction'
  AND state_change < now() - interval '30 minutes';
"
echo "Killed idle transactions at $(date)" >> /var/log/db_auto_remediation.log
⚠ Automation Risks
📊 Production Insight
Start with read-only automation (e.g., alerts) before moving to write actions (e.g., killing queries). Always log and notify when automated actions are taken.
🎯 Key Takeaway
Automated remediation can reduce downtime, but must be carefully designed with safety guards.
● Production incidentPOST-MORTEMseverity: high

The Silent Connection Saturation That Took Down an E-Commerce Site

Symptom
Users saw '500 Internal Server Error' and page load times exceeded 30 seconds.
Assumption
The developer assumed a code deployment caused a memory leak, restarting the app server.
Root cause
A background job opened connections without closing them, saturating the max_connections limit. pg_stat_activity showed hundreds of idle-in-transaction connections.
Fix
Killed idle connections, added a connection pooler (PgBouncer), and set up a Prometheus alert on connection count.
Key lesson
  • Monitor connection count with Prometheus and alert before saturation.
  • Use pg_stat_activity to detect idle-in-transaction connections.
  • Implement connection pooling to handle spikes gracefully.
  • Set up Grafana dashboards for historical connection trends.
  • Automate killing of long-running idle connections with a cron job.
Production debug guideSymptom to Action4 entries
Symptom · 01
High CPU usage on database server
Fix
Check pg_stat_activity for long-running queries; enable pg_stat_statements to identify top time-consuming queries.
Symptom · 02
Slow application response times
Fix
Examine pg_stat_database for high transaction rate or disk I/O; correlate with Prometheus metrics.
Symptom · 03
Replication lag increasing
Fix
Query pg_stat_replication; check network latency and WAL generation rate; alert on lag > threshold.
Symptom · 04
Connection pool exhausted
Fix
Inspect pg_stat_activity for idle connections; kill idle transactions; scale connection pool.
★ Quick Debug Cheat SheetImmediate actions for common PostgreSQL performance issues.
Slow query
Immediate action
Run EXPLAIN ANALYZE on the query.
Commands
SELECT * FROM pg_stat_statements ORDER BY total_time DESC LIMIT 5;
SELECT pid, state, query_start, query FROM pg_stat_activity WHERE state != 'idle' ORDER BY query_start;
Fix now
Terminate the offending query: SELECT pg_terminate_backend(pid);
High connection count+
Immediate action
Check current connections.
Commands
SELECT count(*) FROM pg_stat_activity;
SELECT state, count(*) FROM pg_stat_activity GROUP BY state;
Fix now
Kill idle connections: SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle' AND state_change < now() - interval '5 minutes';
Replication lag+
Immediate action
Check replication status.
Commands
SELECT * FROM pg_stat_replication;
SELECT now() - pg_last_xact_replay_timestamp() AS replication_lag;
Fix now
Investigate network or WAL generation; consider increasing max_wal_size.
ToolPurposeData SourceOutput
pg_stat_activityReal-time process monitoringPostgreSQL system viewsTable of current queries
pg_stat_statementsHistorical query performancePostgreSQL extensionAggregated query stats
PrometheusTime-series metric collectionExporters (e.g., postgres_exporter)Metrics at /metrics
GrafanaVisualization and alertingPrometheus, InfluxDB, etc.Dashboards and alerts
⚙ Quick Reference
6 commands from this guide
FileCommand / CodePurpose
query.sqlSELECT query, calls, total_time, rows1. Understanding pg_stat Views
prometheus.ymlglobal:2. Setting Up Prometheus for PostgreSQL
grafana_query.sqlsum by (state) (pg_stat_activity_count{datname="mydb"})3. Building Grafana Dashboards
alert_rules.ymlgroups:4. Automating Alerts with Prometheus Alertmanager
correlation.sqlSELECT pid, now() - query_start AS duration, query5. Correlating Metrics with Application Performance
kill_idle.shpsql -U pg_monitor -d postgres -c "6. Advanced

Key takeaways

1
pg_stat views are the foundation for PostgreSQL observability; pg_stat_statements is critical for query analysis.
2
Prometheus and Grafana provide a scalable, open-source monitoring stack that turns raw metrics into actionable insights.
3
Automate alerts and remediation to reduce downtime, but always implement safety measures.
4
Correlate database metrics with application performance for faster root cause analysis.

Common mistakes to avoid

3 patterns
×

Not enabling pg_stat_statements

×

Scraping too frequently

×

Ignoring replication lag alerts

INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
How would you identify the top 5 slowest queries in PostgreSQL?
Q02SENIOR
Explain how you would set up Prometheus to monitor PostgreSQL and alert ...
Q03SENIOR
How can you correlate a spike in application latency with database perfo...
Q01 of 03JUNIOR

How would you identify the top 5 slowest queries in PostgreSQL?

ANSWER
Enable pg_stat_statements extension, then query: SELECT query, total_time, calls, rows FROM pg_stat_statements ORDER BY total_time DESC LIMIT 5;
FAQ · 4 QUESTIONS

Frequently Asked Questions

01
What is the difference between pg_stat_activity and pg_stat_statements?
02
How do I monitor replication lag with Prometheus?
03
Can I use this stack with cloud-managed PostgreSQL like RDS?
04
What is the overhead of running postgres_exporter?
N
Naren Founder & Principal Engineer

20+ years shipping high-throughput database systems. Everything here is grounded in real deployments.

Follow
Verified
production tested
July 13, 2026
last updated
2,165
articles · all by Naren
🔥

That's MySQL & PostgreSQL. Mark it forged?

3 min read · try the examples if you haven't

Previous
PostgreSQL 17 and 18: New Features and Migration
19 / 20 · MySQL & PostgreSQL
Next
MySQL 8.4 and 9.0: New Features and Migration