pgtop
A TUI activity monitor for PostgreSQL — htop for your
database. Activity, locks, top queries and replication state in a
single terminal pane you can keep open all day.
The panes
Four views of one connection
Everything PostgreSQL already knows about what it is doing right now
is spread across a handful of system views, and reaching any of them
means writing the query again. pgtop keeps all four open at once and
refreshes them on their own schedules, so switching from “what is
running” to “what is blocking it” is a keystroke rather than another
psql session.
- Activity. A sortable, filterable list of backends from
pg_stat_activity— state, wait event, how long the statement has been running, and the query itself. - Locks. A snapshot of
pg_lockswith granted and waiting state, and the chain of blocking PIDs that leads back to whoever is actually holding things up. - Top queries.
pg_stat_statementsaggregates — calls, mean time, total time, rows. The tab has three honest states: loading, unavailable because the extension isn’t installed, or data. - Replication.
pg_stat_replicationas seen from the primary: client address, state, and lag in bytes at each WAL stage.
Above them the header carries the last sixty seconds of transactions per second, active connections and cache hit ratio as sparklines, so a spike that has already passed is still visible when you look up.
Keys
The keys
Tabs are digits, movement is arrows, and everything destructive is a different shape of key than everything safe.
| Key | Action |
|---|---|
| 1 2 3 4 | switch tabs — activity, locks, top queries, replication |
| ↑ ↓ | move the selection |
| Enter | open the details of the selected backend |
| / | filter — substring across user, database and query |
| s / S | cycle the sort column / toggle the direction |
| g | activity: jump to a pid — type the digits, then Enter |
| c | cancel the running query (pg_cancel_backend) |
| K | terminate the session (pg_terminate_backend) |
| Alt + 1…9 | switch the active connection |
| Esc | close the modal |
| q / Ctrl + C | quit |
The scroll wheel scrolls the active table. Mouse capture is on, so
holding ⇧ while selecting bypasses pgtop and lets the
terminal handle text selection — which works in iTerm, Terminal,
Alacritty, Kitty and GNOME Terminal.
Actions
Cancelling is opt-in, and it is written down
c cancels the running query and K
terminates the whole session. Neither is available unless pgtop was
started with --allow-actions, and both put a modal in
front of you that wants the word yes typed out rather
than a keystroke you could hit by reflex. Every action that goes
through is written to an audit log.
A profile can also set read_only = true, which forces
actions off even when the flag is present. That exists for the case
the flag is meant to protect against in the first place: switching
from the local profile to production without thinking about which
flags are still on the command line.
Connections
More than one database at a time
pgtop prod staging local opens three sessions in one
TUI, switched with ⌥ and a digit. Each connection keeps
its own collectors, its own filter and its own selection, so moving
between them doesn’t cost you the row you were looking at.
A database hiccup doesn’t take the interface down with it. The
reconnect backs off exponentially from 500 ms to a 30-second ceiling
and the title says · connecting #N… while it retries, so
a monitor left open overnight is still a monitor in the morning.
Configuration
Profiles in one TOML file
pgtop reads ~/.config/pgtop/config.toml, or the same
path under $XDG_CONFIG_HOME. A profile is a name, a DSN
and whatever you want to differ about that database; naming it on the
command line is then the whole invocation.
Settings are layered, lowest to highest: built-in defaults, then the
profile, then DATABASE_URL, then the flags you typed.
Themes are dark and light.
default_profile = "local"
[profiles.local]
dsn = "host=localhost user=pgtop password=pgtop port=5433 dbname=pgtop"
[profiles.prod]
dsn = "host=prod-db.example user=monitor dbname=catalog"
# read_only = true forces actions off even with --allow-actions
read_only = true
[ui]
theme = "dark" # or "light"
[intervals]
activity = 1
locks = 1
top_queries = 10
replication = 5
stats = 1
Requirements
What it asks of your database
- A role that can read the statistics views.
GRANT pg_read_all_statsis enough —pg_monitoralso works on PostgreSQL 10 and newer. - For the top queries tab, the
pg_stat_statementsextension installed (a superuser action) andGRANT SELECTon it. Without it the tab says so rather than showing an empty table. - For cancel and terminate, a role that is superuser, owns the backend’s session, or has
pg_signal_backend. Everything else works on a plain read-only role.
TLS
Managed Postgres — RDS, Cloud SQL, Heroku, Supabase — wants TLS, and
pgtop honours all five libpq sslmode values straight
from the DSN.
| sslmode | Behaviour |
|---|---|
disable | no TLS |
prefer | try TLS, fall back to plain — the default |
require | TLS forced, no certificate verification |
verify-ca | TLS forced, chain validated against the bundled Mozilla root store; the hostname is not checked |
verify-full | as verify-ca, and the hostname must match the DSN host |
Custom and self-signed CAs are not supported yet — use
sslmode=require to skip verification, or open an issue
describing the setup.
Get it
Install
Published on crates.io, with pre-built x86_64 and aarch64 binaries for Linux and macOS on every tag.
pgtop on crates.ioSource and releases on github.com/tauvin/pgtop · questions and bug reports
# from source
cargo install pgtop
# pre-built binary, macOS and Linux
curl --proto '=https' --tlsv1.2 -LsSf \
https://github.com/tauvin/pgtop/releases/latest/download/pgtop-installer.sh | sh
# Homebrew
brew install tauvin/pgtop/pgtop
# Docker, multi-arch
docker run --rm -it ghcr.io/tauvin/pgtop:latest --dsn 'postgres://...'
# Nix flake
nix run github:tauvin/pgtop -- --dsn 'postgres://...'
Pointing it at something
A DSN on the command line, DATABASE_URL in the
environment, or the name of a profile — pgtop prod.
Several names open several connections at once.
License
Dual-licensed under Apache-2.0 or MIT, at your option — the usual arrangement in the Rust ecosystem, so it drops into either kind of project without a conversation about licences.