PostgreSQL diagnostics with pgbot
pgbot is a read-only Postgres diagnostic tool. Its findings are computed
deterministically (in Go, not by a model) and it never writes to the
database. Treat pgbot's output as the source of truth. Your job is to run the
right command, then interpret, prioritize, and recommend — never to invent
diagnoses or act on the database.
When to use this skill
- "why is my database slow", "what should I optimize", "is my DB healthy?"
- "which indexes can I drop", "what's using all the disk", "is autovacuum keeping up?"
- "what query is eating the database", "should I re-tune anything?"
- Any Postgres performance / health / bloat / index / vacuum / config question.
How to run it
If the pgbot MCP server is connected, call its tools: inspect (start here),
top_queries, vacuum_health, unused_indexes, why. They return stable JSON.
(The logs, waits, activity, erd, and report commands are CLI-only for
now — run them through the shell.)
Otherwise run the CLI. It needs a connection string for a read-capable role
(ideally one with pg_monitor). Pick the command that matches the question:
| Question |
Command |
| Open-ended / "is it healthy" |
pgbot inspect "$DSN" (health score + worst-first findings) |
| Full detail |
pgbot inspect "$DSN" --full |
| "What's eating the database?" |
pgbot queries "$DSN" (top by total time; --by-calls to re-rank) |
| "What's using disk / biggest tables?" |
pgbot tables "$DSN" (size + seq-vs-index scan = missing-index radar) |
| "Is autovacuum keeping up?" |
pgbot vacuum "$DSN" |
| "Which indexes can I drop?" |
pgbot indexes "$DSN" |
| "Should I re-tune config?" |
pgbot tune "$DSN" |
| "Why is it slow RIGHT NOW?" |
pgbot why --duration 10s "$DSN" (live wait sampling → evidence-gated cause: lock contention vs IO vs CPU vs client; refuses to guess on thin evidence) |
| "Who is connected / what's running?" |
pgbot activity "$DSN" (live sessions: PIDs, states, waits, ages, scrubbed SQL) |
| "Where does database time go?" |
pgbot waits --duration 10s "$DSN" (sampled wait classes + blockers named only with sustained evidence) |
| "What changed since yesterday?" |
pgbot why "$DSN" / pgbot diff --since 24h (offline, from stored snapshots) |
| "What's in the server log?" |
pgbot logs "$DSN" (--live to follow, --level error or --level audit to filter; needs one extra grant, printed when missing) |
| "What does the schema look like?" |
pgbot erd "$DSN" (--mermaid for a renderable diagram, --html > schema.html for an interactive file) |
| Full report for a human to read |
pgbot report "$DSN" > report.html (self-contained page: findings, queries, indexes, waits) |
| Machine-readable for parsing |
pgbot inspect "$DSN" --json |
Add --timeout 60s for large or remote databases.
First run — when a prerequisite is missing
Don't stall and don't improvise; each gap has one fix:
pgbot not on PATH: install it — curl -fsSL https://pgbot.dev/install | sh
(or brew install pgrundev/tap/pgbot, or run without installing via
npx @pgbot/cli). Run it through the normal permission prompt so the user
sees the command.
- No connection string: check
$DATABASE_URL, then ask the user for one.
Never guess or assemble credentials yourself.
- Role or extension gaps: if the installed pgbot has
init, run
pgbot init --verify "$DSN" — it checks pg_monitor and pg_stat_statements
and names each fix. Older builds report the same gaps at connect time.
To create the read-only role, generate the SQL with pgbot init (or use the
Setup section of the pgbot README) and hand it to the user to run as
admin — never execute it yourself: pgbot never writes, and neither do you.
Rules — non-negotiable
- Findings are facts. Do not invent a number, table, index, or query id
beyond what pgbot reports. If pgbot didn't measure it, say what pgbot would
need to collect to find out.
- Carry every caveat into the recommendation. Example: "unused index" scan
counts are per-node — on a primary, a replica may still use an index that
looks unused. pgbot flags this; never recommend dropping an index without
its caveat. (pgbot's
tables/indexes and the JSON make replication state
visible — check it.)
- Never execute the user's query to diagnose it. No
EXPLAIN ANALYZE, no
running the statement "just to time it." Suggest only safe, non-executing
steps (EXPLAIN without ANALYZE is fine; it doesn't run the query).
- Prioritize by impact, not by count. Risk (time-to-incident: wraparound, a
WAL-pinning replication slot, a filling disk) comes first. Then the biggest
latency/storage win. A 9 GiB unused-index reclaim may matter less than one
query eating 60% of DB time — say which to do first and why.
- Never write to the database. pgbot is read-only; you recommend, you don't
act. Hand the user the exact
DROP INDEX CONCURRENTLY / ALTER / VACUUM
statement to run themselves.
- Hedge low confidence. A finding below 0.5 confidence is a possibility
("may", "possibly"), not an assertion.
- Label the evidence. Keep three categories visibly distinct in your
answer: findings proven by pgbot's data, recommendations that still need
review (of code, replicas, or workload), and conclusions blocked by missing
statistics or permissions.
Output shape
- One-line health verdict.
- Worst-first, at most ~3 issues. For each: the problem with pgbot's number →
a likely cause only if the data (deltas/events) supports one → a safe,
concrete recommended step, with its caveat inline.
- Briefly name what's healthy, so the user knows what was checked.
Reading the signals
tables: a large table with heavy seq scans and few idx scans is a
likely missing-index candidate — cross-check it against queries (a top query
filtering that table confirms it).
queries: share is % of total DB execution time. A single query above
~30% is the real hot path; index-dropping won't touch it.
vacuum: due? yes with a stale/never last-autovacuum means autovacuum
is falling behind — the early signal for bloat and, downstream, wraparound.
- replication-slot findings: an inactive slot retaining WAL fills the disk;
treat it as time-to-incident, not cosmetic.
Safety and privacy
pgbot only ever reads. Nothing leaves the machine except pgbot explain /
pgbot ask, which send the PII-free findings (normalized query text, no
literals) to an LLM and say so first. Use a role with pg_monitor; never paste a
production superuser credential where it can leak.
1---2name: postgres-diagnostics3description: Diagnose PostgreSQL health and performance using the read-only pgbot tool. Use for slow databases, expensive queries, unused indexes, disk usage, bloat, autovacuum, vacuum health, and configuration questions.4---56# PostgreSQL diagnostics with pgbot78pgbot is a **read-only** Postgres diagnostic tool. Its findings are computed9**deterministically** (in Go, not by a model) and it **never writes** to the10database. Treat pgbot's output as the source of truth. Your job is to run the11right command, then interpret, prioritize, and recommend — never to invent12diagnoses or act on the database.1314## When to use this skill1516- "why is my database slow", "what should I optimize", "is my DB healthy?"17- "which indexes can I drop", "what's using all the disk", "is autovacuum keeping up?"18- "what query is eating the database", "should I re-tune anything?"19- Any Postgres performance / health / bloat / index / vacuum / config question.2021## How to run it2223**If the pgbot MCP server is connected**, call its tools: `inspect` (start here),24`top_queries`, `vacuum_health`, `unused_indexes`, `why`. They return stable JSON.25(The `logs`, `waits`, `activity`, `erd`, and `report` commands are CLI-only for26now — run them through the shell.)2728**Otherwise run the CLI.** It needs a connection string for a read-capable role29(ideally one with `pg_monitor`). Pick the command that matches the question:3031| Question | Command |32|---|---|33| Open-ended / "is it healthy" | `pgbot inspect "$DSN"` (health score + worst-first findings) |34| Full detail | `pgbot inspect "$DSN" --full` |35| "What's eating the database?" | `pgbot queries "$DSN"` (top by total time; `--by-calls` to re-rank) |36| "What's using disk / biggest tables?" | `pgbot tables "$DSN"` (size + seq-vs-index scan = missing-index radar) |37| "Is autovacuum keeping up?" | `pgbot vacuum "$DSN"` |38| "Which indexes can I drop?" | `pgbot indexes "$DSN"` |39| "Should I re-tune config?" | `pgbot tune "$DSN"` |40| "Why is it slow RIGHT NOW?" | `pgbot why --duration 10s "$DSN"` (live wait sampling → evidence-gated cause: lock contention vs IO vs CPU vs client; refuses to guess on thin evidence) |41| "Who is connected / what's running?" | `pgbot activity "$DSN"` (live sessions: PIDs, states, waits, ages, scrubbed SQL) |42| "Where does database time go?" | `pgbot waits --duration 10s "$DSN"` (sampled wait classes + blockers named only with sustained evidence) |43| "What changed since yesterday?" | `pgbot why "$DSN"` / `pgbot diff --since 24h` (offline, from stored snapshots) |44| "What's in the server log?" | `pgbot logs "$DSN"` (`--live` to follow, `--level error` or `--level audit` to filter; needs one extra grant, printed when missing) |45| "What does the schema look like?" | `pgbot erd "$DSN"` (`--mermaid` for a renderable diagram, `--html > schema.html` for an interactive file) |46| Full report for a human to read | `pgbot report "$DSN" > report.html` (self-contained page: findings, queries, indexes, waits) |47| Machine-readable for parsing | `pgbot inspect "$DSN" --json` |4849Add `--timeout 60s` for large or remote databases.5051## First run — when a prerequisite is missing5253Don't stall and don't improvise; each gap has one fix:5455- **`pgbot` not on PATH:** install it — `curl -fsSL https://pgbot.dev/install | sh`56 (or `brew install pgrundev/tap/pgbot`, or run without installing via57 `npx @pgbot/cli`). Run it through the normal permission prompt so the user58 sees the command.59- **No connection string:** check `$DATABASE_URL`, then ask the user for one.60 Never guess or assemble credentials yourself.61- **Role or extension gaps:** if the installed pgbot has `init`, run62 `pgbot init --verify "$DSN"` — it checks pg_monitor and pg_stat_statements63 and names each fix. Older builds report the same gaps at connect time.64 To create the read-only role, generate the SQL with `pgbot init` (or use the65 Setup section of the pgbot README) and **hand it to the user to run as66 admin** — never execute it yourself: pgbot never writes, and neither do you.6768## Rules — non-negotiable69701. **Findings are facts.** Do not invent a number, table, index, or query id71 beyond what pgbot reports. If pgbot didn't measure it, say what pgbot would72 need to collect to find out.732. **Carry every caveat into the recommendation.** Example: "unused index" scan74 counts are per-node — on a primary, a replica may still use an index that75 looks unused. pgbot flags this; **never recommend dropping an index without76 its caveat.** (pgbot's `tables`/`indexes` and the JSON make replication state77 visible — check it.)783. **Never execute the user's query to diagnose it.** No `EXPLAIN ANALYZE`, no79 running the statement "just to time it." Suggest only safe, non-executing80 steps (`EXPLAIN` without `ANALYZE` is fine; it doesn't run the query).814. **Prioritize by impact, not by count.** Risk (time-to-incident: wraparound, a82 WAL-pinning replication slot, a filling disk) comes first. Then the biggest83 latency/storage win. A 9 GiB unused-index reclaim may matter *less* than one84 query eating 60% of DB time — say which to do first and why.855. **Never write to the database.** pgbot is read-only; you recommend, you don't86 act. Hand the user the exact `DROP INDEX CONCURRENTLY` / `ALTER` / `VACUUM`87 statement to run themselves.886. **Hedge low confidence.** A finding below 0.5 confidence is a possibility89 ("may", "possibly"), not an assertion.907. **Label the evidence.** Keep three categories visibly distinct in your91 answer: findings proven by pgbot's data, recommendations that still need92 review (of code, replicas, or workload), and conclusions blocked by missing93 statistics or permissions.9495## Output shape96971. One-line health verdict.982. Worst-first, at most ~3 issues. For each: the problem *with pgbot's number* →99 a likely cause **only if the data (deltas/events) supports one** → a safe,100 concrete recommended step, with its caveat inline.1013. Briefly name what's healthy, so the user knows what was checked.102103## Reading the signals104105- **`tables`:** a large table with heavy `seq scans` and few `idx scans` is a106 likely missing-index candidate — cross-check it against `queries` (a top query107 filtering that table confirms it).108- **`queries`:** `share` is % of total DB execution time. A single query above109 ~30% is the real hot path; index-dropping won't touch it.110- **`vacuum`:** `due? yes` with a stale/`never` last-autovacuum means autovacuum111 is falling behind — the early signal for bloat and, downstream, wraparound.112- **replication-slot findings:** an inactive slot retaining WAL fills the disk;113 treat it as time-to-incident, not cosmetic.114115## Safety and privacy116117pgbot only ever reads. Nothing leaves the machine except `pgbot explain` /118`pgbot ask`, which send the **PII-free** findings (normalized query text, no119literals) to an LLM and say so first. Use a role with `pg_monitor`; never paste a120production superuser credential where it can leak.