Postgres ops
Personal collection of agent skills for use with Claude Code and other LLM agents.
npx -y skills add andreab67/agent-skills --skill postgres-opsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Operational PostgreSQL workflows for production environments — diagnosing slow queries, lock contention, bloat, replication lag, and connection-pool exhaustion; designing and reviewing backups (pg_dump, pg_basebackup, PITR via WAL archiving); planning upgrades and major-version migrations; configuring pgBouncer/RDS Proxy/PgCat; tuning postgresql.conf for OLTP and analytics workloads; writing and reviewing schema migrations across EF Core, Alembic, Flyway, and raw SQL; setting up observability with postgres_exporter to Prometheus, log shipping to Loki, and slow-query alerting; hardening for DoD/federal use (STIG, role separation, RLS, pgaudit, TLS). Use this skill whenever the user mentions Postgres, PostgreSQL, pg_, EXPLAIN ANALYZE, autovacuum, pgBouncer, replication lag, schema migrations, or anything involving a Postgres incident, performance problem, upgrade, backup, or compliance audit — even if they don't say "Postgres" explicitly but the context is clearly a relational database on PostgreSQL. Do NOT use for greenfield CRUD scaffolding — use nextjs-react-postgres-builder for that.
SKILL.md
13.8 KB, ~3.1k tokens by cl100k_base, as published. Nobody here has run it
postgres-ops
Production-grade PostgreSQL operations: diagnosis, performance, HA/DR, security, and observability. Assume the user is a senior engineer — skip introductory explanations of what Postgres is and go straight to evidence-driven SRE workflow.
When to use
Trigger on operational Postgres tasks:
- Incident diagnosis: slow queries, deadlocks, lock waits, runaway autovacuum, connection exhaustion, replication lag, disk pressure, OOMs.
- Performance review:
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)interpretation, index strategy, partitioning, vacuum/autovacuum tuning,work_mem/shared_bufferssizing. - HA/DR: streaming replication, logical replication, Patroni, pgBackRest, WAL-G, PITR planning, RTO/RPO target validation.
- Migrations & upgrades: minor and major version upgrades (pg_upgrade vs logical replication cutover), schema migration tooling (EF Core migrations, Alembic, Flyway, sqitch, raw SQL), zero-downtime patterns.
- Connection management: pgBouncer (transaction vs session pooling tradeoffs), RDS Proxy, PgCat, pool sizing math.
- Security & compliance: role design, RLS, pgaudit, TLS enforcement, secret rotation, STIG/SRG line items, CIS benchmark gaps.
- Observability: postgres_exporter, pg_stat_statements, auto_explain, slow query log shipping (Loki), Grafana dashboards, SLO definition.
Do NOT trigger for:
- New-feature CRUD scaffolding in Next.js (use
nextjs-react-postgres-builder). - Pure SQL-language questions ("what does LATERAL do") with no operational context.
- Other database engines (MySQL, SQL Server, Cosmos DB).
Instructions
1. Diagnose like an SRE
For any incident or performance complaint, follow hypothesis → evidence → fix → verification. Never propose a fix without naming the query you'd run to confirm the diagnosis first.
Default first-look queries:
-- Active sessions and what they're waiting on
SELECT pid, usename, application_name, state, wait_event_type, wait_event,
now() - query_start AS runtime, left(query, 200) AS query
FROM pg_stat_activity
WHERE state <> 'idle'
ORDER BY runtime DESC NULLS LAST;
-- Blocking chains
SELECT blocked.pid AS blocked_pid, blocked.query AS blocked_query,
blocking.pid AS blocking_pid, blocking.query AS blocking_query
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking ON blocking.pid = ANY(pg_blocking_pids(blocked.pid));
-- Top queries by total time (requires pg_stat_statements)
SELECT round(total_exec_time::numeric, 0) AS total_ms,
calls, round(mean_exec_time::numeric, 2) AS mean_ms,
round((100 * total_exec_time / sum(total_exec_time) OVER ())::numeric, 1) AS pct,
left(query, 200) AS query
FROM pg_stat_statements
ORDER BY total_exec_time DESC LIMIT 20;
-- Bloat / vacuum status
SELECT schemaname, relname, n_live_tup, n_dead_tup,
round(100.0 * n_dead_tup / NULLIF(n_live_tup + n_dead_tup, 0), 1) AS dead_pct,
last_vacuum, last_autovacuum, last_analyze, last_autoanalyze
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY dead_pct DESC NULLS LAST;
-- Replication lag (on primary)
SELECT client_addr, state, sync_state,
pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn) AS sent_lag_bytes,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replay_lag_bytes,
write_lag, flush_lag, replay_lag
FROM pg_stat_replication;
2. Read query plans rigorously
When given an EXPLAIN ANALYZE output:
- Identify row-estimate vs actual mismatch (>10x = stale stats or bad correlation).
- Find the dominant cost node (Seq Scan on large table, nested loop with high outer rows, sort spilling to disk).
- Check buffer numbers — heavy
read=vshit=indicates cold cache or undersizedshared_buffers. - Recommend: index, query rewrite, statistics target bump, or partitioning — in that order of preference.
- Always provide the verification command (re-run with same params and compare).
3. Migration & upgrade discipline
For schema migrations:
- Always show forward and rollback DDL.
- Flag any
ALTER TABLEthat rewrites the table (changing column type, adding NOT NULL without DEFAULT on PG <11, etc.). - For zero-downtime: use the expand-contract pattern (add nullable → backfill → enforce → drop).
- For EF Core: use
Add-Migration+Script-Migrationto review SQL before apply; neverUpdate-Databasein prod.
For version upgrades:
- pg_upgrade with
--linkfor in-place when downtime is acceptable. - Logical replication cutover (pglogical or built-in) for near-zero downtime.
- Always confirm extension compatibility on the target version first.
4. Connection pooling math
For pgBouncer in transaction-pooling mode:
default_pool_sizeper (user, database) ≈max_connections / number_of_poolswith headroom.- App-side pool: keep small (5–20 per replica). The pooler is the real concurrency limit.
- Watch out for: prepared statements (need
pgbouncer >= 1.21withserver_lifetimetuning), session-level features (SET LOCALonly, noLISTEN/NOTIFY, no temp tables across txns).
5. Federal/DoD posture (when relevant)
- Enforce TLS with
ssl=on,ssl_min_protocol_version=TLSv1.2, restricthostsslonly inpg_hba.conf. pgauditextension for STIG-required audit logging; ship logs to a tamper-resistant store.- Separate roles: no shared accounts, no
SUPERUSERfor app roles, RLS for tenant isolation. - FIPS-validated OpenSSL on the host; verify with
SHOW ssl_library;and OS-level FIPS mode. - Check the current PostgreSQL STIG (DISA) for line-item compliance — versions ship updates regularly.
6. Observability defaults
- Enable
pg_stat_statements,auto_explain(withlog_min_duration_statementreasonable for prod, e.g., 1000ms). - Run
postgres_exporteras a sidecar; scrape into Prometheus. - Ship CSV logs to Loki via Promtail or Vector; Grafana dashboards keyed on
pg_stat_statementsandpg_stat_activity. - SLO suggestion: P95 query latency for the top-N statements, plus replication lag <N seconds.
7. Output discipline
- Give complete, runnable SQL or shell — no placeholders.
- Call out destructive operations explicitly (
DROP,TRUNCATE,pg_upgrade --link,vacuum full). - For any tuning parameter recommendation, state the workload assumption (OLTP / analytics / mixed) and the math behind it.
- When uncertain about a version-specific behavior, say so and name the version where the behavior changed.
Anti-patterns
These look like reasonable Postgres moves but will either corrupt data, cause silent failures, or surprise you in production:
- Running
VACUUM FULLon a live high-traffic table —VACUUM FULLacquires an exclusive lock that blocks all reads and writes for the duration. On large tables this means minutes of downtime. Use regularVACUUM(autovacuum) for routine bloat;VACUUM FULLonly on an offline table or during a maintenance window. - Using
UPDATE-Databasein EF Core directly against production — EF Core's migration runner will execute DDL without the ability to inspect SQL first, and there is no dry-run mode. Always generate a SQL script withScript-Migration, review it, then apply through a controlled change window. - Setting
work_memglobally high —work_memis per sort operation per query, and a single complex query can trigger many operations simultaneously. Settingwork_mem = 1GBon a 64 GB server with 100 connections doing complex sorts will OOM the host. Set it low globally and override per session for known heavy queries. - Trusting
pg_dumpwithout a restore test —pg_dumpcompleting successfully does not mean the backup is usable. Schema dumps with extension version mismatches or missing roles fail silently on restore. Test a full restore to a separate instance on a schedule — not right before you need it. - Using
transactionpooling mode in pgBouncer with prepared statements — prepared statements are session-scoped; in transaction pooling the server-side connection changes between transactions, soPREPARE/EXECUTEwill reference a statement that no longer exists. Either usesessionpooling or move toDEALLOCATE ALLpatterns on every transaction. - Skipping the
--linkcaveat withpg_upgrade—pg_upgrade --linkcreates hard links rather than copying data files, making the upgrade fast. But if the old cluster is accessed or the upgrade is rolled back after the new cluster has written to the linked files, data corruption results. Backup before--linkand never start the old cluster again after the new one has written data. - Adding an index without
CONCURRENTLYon a production table — standardCREATE INDEXholds aShareLockthat blocks writes for the index build duration.CREATE INDEX CONCURRENTLYavoids the lock but takes longer and cannot run inside a transaction block.
Error Handling
Domain-specific failure modes when running the diagnostic/fix workflows above:
- Can't connect to diagnose
max_connectionsexhaustion — the "check connections" query itself fails withFATAL: sorry, too many clients already. Detection: connection refused citingmax_connections. Recovery: connect via the reserved superuser slot (superuser_reserved_connections, default 3) with a superuser role, or via a local Unix-socketpsqlon the host, which usually isn't gated the same way; from there, querypg_stat_activityand terminate the worst idle-in-transaction offenders withpg_terminate_backend(pid). EXPLAIN ANALYZEon a write query actually executes it — running it against anUPDATE/DELETE/INSERTperforms the write for real; there's no dry-run mode. Detection: none after the fact — the write is already committed if not caught first. Recovery: wrap it inBEGIN; EXPLAIN ANALYZE ...; ROLLBACK;so the plan runs but nothing commits, or run it against a replica/staging copy for anything destructive.pg_stat_statementsrelation doesn't exist — the "top queries by total time" query fails withrelation "pg_stat_statements" does not exist. Detection: that literal error. Recovery: it must be inshared_preload_libraries, which needs a full instance restart, not justCREATE EXTENSION— checkSHOW shared_preload_libraries;first, and if it's missing, schedule a restart window before promising this data.CREATE INDEX CONCURRENTLYfails partway through — leaves anINVALIDindex behind instead of rolling back cleanly (concurrent builds can't run in a transaction, so a failure or cancel doesn't undo it). Detection:pg_index.indisvalid = falsefor the new index, or\d <table>shows it present but marked invalid. Recovery:DROP INDEX CONCURRENTLY <name>;then retry — ideally after identifying what killed it (lock timeout, deadlock, disk full).pg_upgrade --checkfails on extension mismatch — reports incompatible extension versions or missing objects before anything is touched. Detection: check-mode output (always run--checkfirst, never skip it). Recovery: update/reinstall the flagged extensions on the target version's cluster, re-run--checkuntil clean, then perform the real upgrade.- pgBouncer transaction pooling clients see
prepared statement "S_1" already exists/does not exist— surfaces once app code or an ORM uses server-side prepared statements. Detection: that literal error, only in transaction pooling mode. Recovery: switch the pool tosessionmode for that database, or disable server-side prepared statements at the driver (e.g., NpgsqlMax Auto Prepare=0, psycopgprepare_threshold=None). - Replication lag query returns zero rows —
pg_stat_replicationis empty on what you thought was the primary. Detection: zero rows, not an error. Recovery: confirm you're actually on the primary (SELECT pg_is_in_recovery();should returnfalse); if it does and the view is still empty, no replicas are currently connected — check the replica's own logs for the connection failure (auth,pg_hba.conf, network reachability).
Example prompts
- "We have a query taking 30 seconds in prod. Here's the EXPLAIN ANALYZE — what's wrong?"
- "Our app hit max_connections. Walk me through diagnosing the cause and fixing it without downtime."
- "I need to add a NOT NULL column to a 200M-row table. What's the zero-downtime approach?"
- "We're upgrading from Postgres 14 to 16. What's the fastest path and what should I check first?"
- "Help me size pgBouncer pool for 50 app instances hitting a single primary."
- "Our DISA STIG audit is next week. What Postgres controls do I need in place?"
- "Autovacuum is running constantly on one table. How do I tune it?"
Related skills
k8s-nextjs-deploy— Kubernetes deployment patterns if Postgres runs in-clusterubuntu24-stig— OS-level STIG hardening for the host running Postgres
Gives 0 of the 12 instructions most audit compliance skills give in ~3.1k tokens
Counted across 936 of the 1,487 authors here whose files we hold, read 2026-08-06
- group findings by severityin 44 of 936
- Fetch latest guidelines before each reviewin 43 of 936, across 3 files
- Check files against all fetched rulesin 42 of 936, across 2 files
- Output findings in terse file:line formatin 41 of 936, across 3 files
- Ask user which files to review if none specifiedin 41 of 936, across 3 files
- Read specified files or prompt user for filesin 39 of 936, across 1 file
- generate the audit reportin 39 of 936, across 36 files
- assign a severity to every findingin 25 of 936
- run automated accessibility scansin 23 of 936, across 13 files
- map findings to WCAG criteriain 20 of 936, across 10 files
- confirm audit scopein 19 of 936, across 9 files
- check title tags and meta descriptions for uniquenessin 19 of 936, across 5 files
Said here and by no other author read
- Follow hypothesis, evidence, fix, verification
- Run diagnostic queries before proposing fixes
- Analyze EXPLAIN output for estimate mismatches
- Provide forward and rollback DDL for migrations
- Flag table-rewriting ALTER TABLE operations
- State workload assumptions for tuning parameters
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.