Every RDS PostgreSQL instance ships with three layers of native observability: CloudWatch metrics, Performance Insights, and — for the adventurous — DevOps Guru for RDS. Together they answer "what is happening" reasonably well. They almost never answer "why," and they rarely tell you what to do about it before users notice. This post breaks down exactly where each native tool stops, and where a Postgres-native monitoring layer needs to pick up.
What AWS Gives You Out of the Box
- CloudWatch metrics — infrastructure-level counters (CPUUtilization, FreeableMemory, ReadIOPS, WriteIOPS, DatabaseConnections, ReplicaLag) at 1-minute granularity by default, 1-second with Enhanced Monitoring enabled.
- Performance Insights — a rolling view of top SQL by load, broken down by wait event, with 7 days of history free and up to 24 months on the paid tier.
- Enhanced Monitoring — OS-level metrics (per-process CPU, disk, memory) collected by an agent running on the RDS host, billed separately.
- DevOps Guru for RDS — ML-based anomaly detection on top of the metrics above, flags deviations from a learned baseline.
This is a solid foundation. It is also, by AWS's own design, generic: it was built to work identically for MySQL, MariaDB, PostgreSQL and SQL Server. Genericity has a cost — none of it understands Postgres internals.
Where CloudWatch Stops
ReplicaLag in CloudWatch is a single number in seconds, sampled every minute. It tells you lag exists right now. It does not tell you:
- Whether the lag is caused by a long-running query holding a snapshot on the replica, network throughput, or the replica falling behind on WAL replay.
- Whether lag is trending upward and will breach your SLA in 20 minutes, or is a one-off spike that will self-correct.
- Which specific standby, if you run more than one, is at risk.
Gotcha: CloudWatch's ReplicaLag metric can read 0 even while logical replication slots are silently accumulating WAL on the primary because a subscriber is stuck — CloudWatch has no visibility into slot-level retention, only physical streaming lag.
Where Performance Insights Stops
Performance Insights is genuinely useful for "what SQL is consuming the most active session time right now." But it is a point-in-time load viewer, not a regression detector:
- It shows current top SQL by wait event, not "this specific query's execution plan changed from an Index Scan to a Seq Scan three days ago."
- It has no concept of a query's historical plan, so a plan flip caused by a stale statistic or an autovacuum that never ran goes unnoticed until load is already high enough to show up in the dashboard.
- It does not track table or index bloat, dead tuple ratios, or autovacuum effectiveness — a top contributor to the exact CPU and IO spikes Performance Insights shows you after the fact.
- Long-term retention (beyond 7 days) is a paid add-on billed per vCPU per instance, which adds up fast across a fleet.
Where DevOps Guru Stops
DevOps Guru's anomaly detection is trained on infrastructure-level metrics (the CloudWatch set). It is good at "CPU is 3 standard deviations above its usual pattern for this time of day." It has no model of Postgres-specific failure modes: transaction ID wraparound risk, connection pool exhaustion by query type, a missing index that would collapse a sequential scan into an index scan, or a replication slot that is about to fill the WAL disk. Those require domain knowledge about how Postgres actually works, not generic time-series anomaly detection.
Side-by-Side
| Capability | CloudWatch + PI + DevOps Guru | PG Monitoring |
|---|---|---|
| Infra metrics (CPU, IOPS, memory) | Yes, native | Yes, plus correlated with query activity |
| Query plan regression detection | No | Yes — tracks EXPLAIN plans over time per query fingerprint |
| Replication lag root cause | Lag value only | Lag + cause (WAL replay, long snapshot, network, slot backlog) + trend prediction |
| Autovacuum / bloat tracking | No | Per-table dead tuple ratio, bloat estimate, vacuum effectiveness |
| Index recommendations | No | AI-generated, with quantified before/after cost and safe DDL |
| Multi-instance / multi-cloud view | Per-instance, AWS-only | Single dashboard across RDS, Aurora, self-managed, other clouds |
| Retention beyond 7 days | Paid tier, per-vCPU billing | Included |
| AI assistant with DB context | No | Copilot with schema, query history and incident context |
Setting Up Continuous Monitoring on RDS
Because RDS does not grant filesystem or superuser access, a push-based agent is the practical model: it connects over the standard PostgreSQL wire protocol (no OS agent required on the RDS host itself) and reads from views already exposed by RDS:
-- Enable in the parameter group (requires a reboot if not already set)
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.track = 'all'
track_io_timing = on
With those enabled, a monitoring role with pg_monitor (the built-in Postgres role, available on RDS since Postgres 10) can read pg_stat_statements, pg_stat_activity, pg_stat_replication and friends without superuser:
CREATE ROLE pg_monitoring_agent WITH LOGIN PASSWORD '<redacted>';
GRANT pg_monitor TO pg_monitoring_agent;
GRANT pg_read_all_stats TO pg_monitoring_agent;
Point the agent at the RDS endpoint, open the security group to the agent's egress IP on port 5432, and metrics start flowing on the same push model CloudWatch uses — except the payload includes plan-level and bloat-level detail CloudWatch never collects.
The Bottom Line
CloudWatch, Performance Insights and DevOps Guru are not wrong — they are exactly what a database-agnostic cloud platform can reasonably ship. The gap they leave is precisely where Postgres-specific expertise matters: plan regressions, vacuum health, replication internals, and recommendations you can act on instead of just numbers to stare at. PG Monitoring runs alongside your existing CloudWatch setup — it does not replace your AWS billing dashboards, it fills in everything AWS structurally cannot know about how Postgres itself is behaving.