RDS PostgreSQL Diagnostics
When to use
Any RDS PostgreSQL investigation where the console alone is insufficient — instance launch failures, vacuum/autovacuum issues, table bloat, connection pooling, pg_stat analysis, connectivity, parameter tuning, physical and logical replication, backup/recovery, extensions, RDS Proxy, encryption, or upgrade troubleshooting.
Investigation workflow
Step 1 — Collect and triage
aws rds describe-db-instances --db-instance-identifier <instance-id>
aws rds describe-events --source-identifier <instance-id> --source-type db-instance --duration 1440
aws rds describe-db-log-files --db-instance-identifier <instance-id>
aws rds download-db-log-file-portion --db-instance-identifier <instance-id> --log-file-name error/postgresql.log
aws rds describe-db-parameters --db-parameter-group-name <param-group>
Step 2 — Performance deep dive
aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name CPUUtilization \
--dimensions Name=DBInstanceIdentifier,Value=<instance-id> \
--start-time <start> --end-time <end> --period 300 --statistics Average
aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name FreeableMemory ...
aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name ReadIOPS ...
aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name WriteIOPS ...
aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name DatabaseConnections ...
aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name TransactionLogsDiskUsage ...
aws pi get-resource-metrics --service-type RDS \
--identifier db-<resource-id> \
--metric-queries '[{"Metric":"db.load.avg"}]' \
--start-time <start> --end-time <end> --period-in-seconds 300
Step 3 — PostgreSQL-specific diagnostics (via psql)
-- Active queries and wait events
SELECT pid, usename, state, wait_event_type, wait_event, query_start, query
FROM pg_stat_activity WHERE state != 'idle' ORDER BY query_start;
-- Table bloat and dead tuples
SELECT schemaname, relname, n_live_tup, n_dead_tup,
ROUND(n_dead_tup::numeric / NULLIF(n_live_tup,0) * 100, 2) AS dead_pct,
last_vacuum, last_autovacuum, last_analyze
FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 20;
-- Connection usage
SELECT count(*) AS total, state FROM pg_stat_activity GROUP BY state;
SHOW max_connections;
-- Replication status (on primary)
SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn,
pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes
FROM pg_stat_replication;
Read references/guardrails.md before concluding on any RDS PostgreSQL issue.
Tool quick reference
| Tool / API |
When to use |
describe-db-instances |
Instance config, engine version, storage |
describe-events |
Recent RDS events |
describe-db-parameters |
Parameter group settings |
download-db-log-file-portion |
PostgreSQL error log |
CloudWatch CPUUtilization |
CPU usage |
CloudWatch FreeableMemory |
Available memory (shared_buffers pressure) |
CloudWatch TransactionLogsDiskUsage |
WAL disk usage |
Performance Insights |
DB load, wait events, top SQL |
pg_stat_activity |
Active queries and connections |
pg_stat_user_tables |
Table stats, vacuum status, dead tuples |
pg_stat_replication |
Replication lag |
pg_stat_bgwriter |
Checkpoint and bgwriter stats |
Gotchas: RDS PostgreSQL
shared_buffers defaults to {DBInstanceClassMemory/32768} (≈25% of memory). PostgreSQL relies on OS cache for the rest. Do not set shared_buffers > 40% of instance memory.
- No
rds_superuser is not the same as PostgreSQL superuser. Some operations (e.g., CREATE EXTENSION, pg_terminate_backend) work, but OS-level access and some superuser-only functions are restricted.
- Autovacuum is critical. Disabling or under-tuning autovacuum leads to table bloat, transaction ID wraparound, and performance degradation. Monitor
n_dead_tup and last_autovacuum.
- Transaction ID wraparound: PostgreSQL uses 32-bit transaction IDs. If autovacuum cannot keep up, the database enters read-only mode at 2 billion transactions. Monitor
age(datfrozenxid).
max_connections default is LEAST({DBInstanceClassMemory/9531392}, 5000). Each connection uses ~10MB. Use connection pooling (RDS Proxy or PgBouncer) for high-connection workloads.
- Logical replication requires
rds.logical_replication=1 and wal_level=logical. This increases WAL generation. Not all data types and DDL are replicated.
- Extensions must be allowlisted by RDS. Use
SHOW rds.allowed_extensions or check aws rds describe-db-engine-versions --engine postgres --engine-version <ver> --query 'DBEngineVersions[0].SupportedFeatureNames'.
- PITR restores to a new instance. WAL-based, so restore time depends on WAL volume since last snapshot.
- Major version upgrades (e.g., 14→16) use pg_upgrade internally. Extensions must be compatible with the target version.
Anti-hallucination rules
- Always cite specific AWS CLI output, CloudWatch metrics, Performance Insights data, or PostgreSQL query results as evidence.
- No OS-level access. Never suggest editing
postgresql.conf or pg_hba.conf directly.
rds_superuser is not PostgreSQL superuser. Some superuser-only functions are restricted.
- PITR creates a new instance. Never suggest in-place restore.
- Autovacuum should never be disabled. Never suggest turning off autovacuum globally.
- Spend no more than 2 minutes on any single hypothesis. Pivot if inconclusive.
Runbooks
| Category |
IDs |
Covers |
| A — Instance |
A1 |
Launch failures |
| B — Performance |
B1-B3 |
Vacuum/bloat, connection pooling, pg_stat analysis |
| C — Connectivity |
C1 |
Connection failures |
| D — Parameters |
D1 |
Parameter group issues |
| E — Replication |
E1-E2 |
Physical replication, logical replication |
| F — Backup |
F1 |
Backup and PITR |
| G — Extensions |
G1 |
Extension management |
| H — Upgrades |
H1 |
Version upgrades |
| I — Proxy |
I1 |
RDS Proxy |
| J — Security |
J1 |
Encryption |
| Z — Catch-All |
Z1 |
General troubleshooting |
1---2name: rds-postgresql-diagnostics3description: Use this skill to investigate and troubleshoot Amazon RDS for PostgreSQL problems by analyzing instance configurations, performance metrics, connectivity, parameter groups, and following structured runbooks. Activate when: launch failures, vacuum/bloat issues, connection pooling, pg_stat analysis, connectivity failures, parameter group tuning, replication lag, logical replication, backup/PITR, extensions, RDS Proxy, encryption, upgrades, or the user says something is wrong with RDS PostgreSQL.4---56# RDS PostgreSQL Diagnostics78## When to use910Any RDS PostgreSQL investigation where the console alone is insufficient — instance launch failures, vacuum/autovacuum issues, table bloat, connection pooling, pg_stat analysis, connectivity, parameter tuning, physical and logical replication, backup/recovery, extensions, RDS Proxy, encryption, or upgrade troubleshooting.1112## Investigation workflow1314### Step 1 — Collect and triage1516```17aws rds describe-db-instances --db-instance-identifier <instance-id>18aws rds describe-events --source-identifier <instance-id> --source-type db-instance --duration 144019aws rds describe-db-log-files --db-instance-identifier <instance-id>20aws rds download-db-log-file-portion --db-instance-identifier <instance-id> --log-file-name error/postgresql.log21aws rds describe-db-parameters --db-parameter-group-name <param-group>22```2324### Step 2 — Performance deep dive2526```27aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name CPUUtilization \28 --dimensions Name=DBInstanceIdentifier,Value=<instance-id> \29 --start-time <start> --end-time <end> --period 300 --statistics Average30aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name FreeableMemory ...31aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name ReadIOPS ...32aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name WriteIOPS ...33aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name DatabaseConnections ...34aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name TransactionLogsDiskUsage ...35aws pi get-resource-metrics --service-type RDS \36 --identifier db-<resource-id> \37 --metric-queries '[{"Metric":"db.load.avg"}]' \38 --start-time <start> --end-time <end> --period-in-seconds 30039```4041### Step 3 — PostgreSQL-specific diagnostics (via psql)4243```sql44-- Active queries and wait events45SELECT pid, usename, state, wait_event_type, wait_event, query_start, query46FROM pg_stat_activity WHERE state != 'idle' ORDER BY query_start;4748-- Table bloat and dead tuples49SELECT schemaname, relname, n_live_tup, n_dead_tup,50 ROUND(n_dead_tup::numeric / NULLIF(n_live_tup,0) * 100, 2) AS dead_pct,51 last_vacuum, last_autovacuum, last_analyze52FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 20;5354-- Connection usage55SELECT count(*) AS total, state FROM pg_stat_activity GROUP BY state;56SHOW max_connections;5758-- Replication status (on primary)59SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn,60 pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes61FROM pg_stat_replication;62```6364Read `references/guardrails.md` before concluding on any RDS PostgreSQL issue.6566## Tool quick reference6768| Tool / API | When to use |69|------------|-------------|70| `describe-db-instances` | Instance config, engine version, storage |71| `describe-events` | Recent RDS events |72| `describe-db-parameters` | Parameter group settings |73| `download-db-log-file-portion` | PostgreSQL error log |74| `CloudWatch CPUUtilization` | CPU usage |75| `CloudWatch FreeableMemory` | Available memory (shared_buffers pressure) |76| `CloudWatch TransactionLogsDiskUsage` | WAL disk usage |77| `Performance Insights` | DB load, wait events, top SQL |78| `pg_stat_activity` | Active queries and connections |79| `pg_stat_user_tables` | Table stats, vacuum status, dead tuples |80| `pg_stat_replication` | Replication lag |81| `pg_stat_bgwriter` | Checkpoint and bgwriter stats |8283## Gotchas: RDS PostgreSQL8485- `shared_buffers` defaults to `{DBInstanceClassMemory/32768}` (≈25% of memory). PostgreSQL relies on OS cache for the rest. Do not set shared_buffers > 40% of instance memory.86- No `rds_superuser` is not the same as PostgreSQL `superuser`. Some operations (e.g., `CREATE EXTENSION`, `pg_terminate_backend`) work, but OS-level access and some superuser-only functions are restricted.87- Autovacuum is critical. Disabling or under-tuning autovacuum leads to table bloat, transaction ID wraparound, and performance degradation. Monitor `n_dead_tup` and `last_autovacuum`.88- Transaction ID wraparound: PostgreSQL uses 32-bit transaction IDs. If autovacuum cannot keep up, the database enters read-only mode at 2 billion transactions. Monitor `age(datfrozenxid)`.89- `max_connections` default is `LEAST({DBInstanceClassMemory/9531392}, 5000)`. Each connection uses ~10MB. Use connection pooling (RDS Proxy or PgBouncer) for high-connection workloads.90- Logical replication requires `rds.logical_replication=1` and `wal_level=logical`. This increases WAL generation. Not all data types and DDL are replicated.91- Extensions must be allowlisted by RDS. Use `SHOW rds.allowed_extensions` or check `aws rds describe-db-engine-versions --engine postgres --engine-version <ver> --query 'DBEngineVersions[0].SupportedFeatureNames'`.92- PITR restores to a new instance. WAL-based, so restore time depends on WAL volume since last snapshot.93- Major version upgrades (e.g., 14→16) use pg_upgrade internally. Extensions must be compatible with the target version.9495## Anti-hallucination rules96971. Always cite specific AWS CLI output, CloudWatch metrics, Performance Insights data, or PostgreSQL query results as evidence.982. No OS-level access. Never suggest editing `postgresql.conf` or `pg_hba.conf` directly.993. `rds_superuser` is not PostgreSQL `superuser`. Some superuser-only functions are restricted.1004. PITR creates a new instance. Never suggest in-place restore.1015. Autovacuum should never be disabled. Never suggest turning off autovacuum globally.1026. Spend no more than 2 minutes on any single hypothesis. Pivot if inconclusive.103104## Runbooks105106| Category | IDs | Covers |107|----------|-----|--------|108| A — Instance | A1 | Launch failures |109| B — Performance | B1-B3 | Vacuum/bloat, connection pooling, pg_stat analysis |110| C — Connectivity | C1 | Connection failures |111| D — Parameters | D1 | Parameter group issues |112| E — Replication | E1-E2 | Physical replication, logical replication |113| F — Backup | F1 | Backup and PITR |114| G — Extensions | G1 | Extension management |115| H — Upgrades | H1 | Version upgrades |116| I — Proxy | I1 | RDS Proxy |117| J — Security | J1 | Encryption |118| Z — Catch-All | Z1 | General troubleshooting |