PostgreSQL Administration (DBA / Server Operations)
Overview
This skill covers operating a PostgreSQL server: configuring it, securing access,
backing it up, replicating it, upgrading it, and watching it run. It is about the
server and cluster, not about writing application SQL.
Mental model — the cluster hierarchy:
PostgreSQL instance (one postmaster, one data directory $PGDATA, one port)
└── cluster = all databases managed by that instance
├── global objects: roles/users, tablespaces, replication slots (shared, cluster-wide)
└── database → schema → object (table/view/sequence/function)
- One running server (
postgres/postmaster) serves many databases; roles and
tablespaces are cluster-global, not per-database.
- Configuration lives in
postgresql.conf + postgresql.auto.conf in $PGDATA;
authentication in pg_hba.conf; runtime state in the pg_stat_* views.
- The server is a long-running daemon: most admin actions are reload (cheap, no
downtime) or, for a minority of settings, restart (brief downtime).
- Write-ahead log (WAL) is the foundation of crash recovery, backups (PITR), and
replication — almost everything operational ties back to it.
Related skills (disambiguation)
| If you need… |
Use skill |
| Server config, roles/auth, backups, replication, upgrades, monitoring |
postgres-admin (this skill) |
The psql client, meta-commands (\d, \dt), scripting the shell |
psql |
| SQL syntax, DDL/DML, data types, functions, query writing |
postgres-sql |
Query tuning, EXPLAIN, indexes, autovacuum mechanics/tuning |
postgres-performance |
contrib extensions (pg_stat_statements, postgis, pgcrypto, …) |
postgres-extensions |
Overlap notes: this skill covers the server-config / GUC side of vacuum, checkpoints,
and WAL (the knobs and what they mean operationally); deep autovacuum/query tuning lives in
postgres-performance. pg_stat_statements is a contrib module — monitoring with it is in
postgres-extensions; this skill points you at the built-in pg_stat_* views.
Version awareness
PostgreSQL ships one major version per year (pg10 = 2017 … pg18 = 2025 stable,
pg19 = 2026 beta). This skill tags features added in PostgreSQL 10 or later inline as
(pgNN+) only where a release note sources it; anything from the 9.x era or earlier is
bedrock and left untagged. Full sourced map: references/version-features.md.
Check what you're running (annotations are a hard floor — a (pg17+) feature simply does
not exist on pg16):
psql -c "SHOW server_version" # e.g. 18.1
psql -c "SELECT version()" # full build string
postgres --version # the server binary
pg_config --version # build/devel version
psql -c "SHOW server_version_num" # 180001 → easy to compare numerically
1. Server configuration
Settings are GUCs (Grand Unified Configuration). Three layers, last-wins:
postgresql.conf → postgresql.auto.conf (written by ALTER SYSTEM) → per-session SET.
SHOW shared_buffers; -- one setting
SHOW all; -- everything
SELECT name, setting, unit, context, pending_restart
FROM pg_settings WHERE name = 'work_mem';
SELECT name, setting, source, sourcefile, sourceline -- where did this value come from?
FROM pg_settings WHERE source NOT IN ('default','override');
ALTER SYSTEM SET work_mem = '64MB'; -- persists to postgresql.auto.conf (since 9.4)
ALTER SYSTEM RESET work_mem; -- remove the override
SELECT pg_reload_conf(); -- apply reload-able changes, no downtime
SET work_mem = '128MB'; -- this session only
RESET work_mem; -- back to the configured value
pg_ctl reload -D $PGDATA # SIGHUP — apply reloadable settings (also: systemctl reload)
pg_ctl restart -D $PGDATA # required for some settings (see below)
Reload vs restart — a setting's context in pg_settings tells you:
context |
How to apply |
Examples |
user / superuser |
SET in-session, or reload |
work_mem, statement_timeout |
sighup |
reload (pg_reload_conf()) |
log_*, autovacuum, archive_command, checkpoint_timeout, max_wal_size |
postmaster |
restart |
shared_buffers, max_connections, listen_addresses, port, wal_level, shared_preload_libraries |
After editing, pending_restart = true in pg_settings flags settings that need a restart.
ALTER SYSTEM cannot set a few bootstrap params (e.g. it warns on settings that have no
effect post-start). Full GUC tour, key parameters, and units: references/config.md.
2. Roles & privileges
Roles are cluster-global. A "user" is just a role WITH LOGIN.
CREATE ROLE app LOGIN PASSWORD 'secret'; -- a login user
CREATE ROLE readonly NOLOGIN; -- a group role
CREATE ROLE deployer LOGIN CREATEDB CREATEROLE;
ALTER ROLE app VALID UNTIL '2027-01-01' CONNECTION LIMIT 20;
ALTER ROLE app SET search_path = app, public; -- per-role GUC default
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly; -- existing tables only
ALTER DEFAULT PRIVILEGES IN SCHEMA public -- future tables too
GRANT SELECT ON TABLES TO readonly;
GRANT readonly TO app; -- role membership
REVOKE INSERT ON orders FROM app;
- Privileges don't apply retroactively —
GRANT … ON ALL TABLES covers tables that exist
now; use ALTER DEFAULT PRIVILEGES for objects created later (and it's per-creator).
public schema hardening (pg15+): new clusters no longer grant CREATE on public
to PUBLIC (CVE-2018-1058 mitigation). On upgraded clusters, consider
REVOKE CREATE ON SCHEMA public FROM PUBLIC yourself.
- Predefined roles grant capabilities without superuser:
pg_monitor,
pg_read_all_settings, pg_read_all_stats, pg_signal_backend, pg_read_all_data /
pg_write_all_data (pg14+), pg_database_owner (pg14+), pg_checkpoint (pg15+),
pg_use_reserved_connections (pg16+), pg_create_subscription (pg16+), pg_maintain
(pg17+, with the MAINTAIN privilege), pg_signal_autovacuum_worker (pg18+).
- Role membership options (pg16+):
GRANT g TO u WITH INHERIT {TRUE|FALSE}, SET {TRUE|FALSE}, ADMIN {TRUE|FALSE} — membership inheritance is now per-grant, not just the
member's INHERIT attribute. (Pre-16, new memberships always inherited per the member role.)
Full role attribute / GRANT / object-privilege reference: references/auth-roles.md.
3. Authentication (pg_hba.conf)
pg_hba.conf is matched top-to-bottom, first match wins (no fall-through). Format:
# TYPE DATABASE USER ADDRESS METHOD
local all all peer # Unix socket, OS-user match
host all all 127.0.0.1/32 scram-sha-256 # TCP, localhost
hostssl app app 10.0.0.0/8 scram-sha-256 # require TLS
host all all 0.0.0.0/0 reject # deny the rest
psql -c "SELECT pg_reload_conf()" # pg_hba changes apply on RELOAD, not restart
psql -c "TABLE pg_hba_file_rules" # see parsed rules + any errors (pg10+)
psql -c "TABLE pg_ident_file_mappings" # parsed pg_ident.conf (pg15+)
- Methods:
scram-sha-256 (pg10+, the modern default — set password_encryption = scram-sha-256, the default since pg14), md5 (legacy, deprecated pg18+), peer
(local OS user), cert (TLS client cert), ldap, gss/sspi, radius, trust (no
auth — dev only), reject, and oauth (pg18+). pg_ident.conf maps OS/external names to
DB roles for peer/cert/gss/ldap.
- Order matters: put specific rules above general ones. A leading
trust line shadows
everything below it.
Methods, TLS setup, and pg_ident.conf mapping: references/auth-roles.md.
4. Backup & recovery
Two families: logical (SQL/dump, portable, per-DB) and physical (file/block-level,
whole cluster, enables PITR).
# Logical — pg_dump (one database). Custom/dir formats are compressed + allow selective,
# parallel restore; plain text is a .sql script.
pg_dump -Fc -f app.dump app # custom format (restore with pg_restore)
pg_dump -Fd -j4 -f app_dir app # directory format, 4 parallel workers
pg_dump --section=pre-data -f schema.sql app
pg_restore -d app -j4 app.dump # parallel restore
pg_dump -t orders -t 'audit_*' app # selected tables
pg_dumpall --globals-only > globals.sql # roles + tablespaces (NOT in pg_dump)
pg_dumpall -f whole_cluster.sql # every DB + globals (text; non-text formats pg19+)
# Physical — pg_basebackup (whole cluster, byte-exact; basis for replicas & PITR)
pg_basebackup -D /backup/base -Ft -z -P # tar + gzip, with progress
pg_basebackup -D /backup/base -X stream -c fast # include WAL via a 2nd stream
# Incremental backup (pg17+) — needs summarize_wal=on; combine to restore
pg_basebackup -D /backup/incr --incremental=/backup/base/backup_manifest
pg_combinebackup /backup/base /backup/incr -o /restore/full # (pg17+)
# Verify (pg13+): manifests are written automatically by pg_basebackup
pg_verifybackup /backup/base
Point-in-time recovery (PITR): restore a base backup, then provide a restore_command
and a recovery_target_*, drop a recovery.signal file, and start the server. The old
recovery.conf was removed in pg12 — recovery settings now live in postgresql.conf with
standby.signal / recovery.signal controlling the mode. Full PITR + upgrade procedures:
references/backup-recovery.md.
5. WAL & replication
SHOW wal_level; -- minimal | replica (default) | logical
SHOW synchronous_commit; -- on | remote_apply | remote_write | local | off
SELECT * FROM pg_stat_replication; -- on the primary: connected standbys + lag
SELECT * FROM pg_replication_slots; -- slots (prevent WAL removal until consumed)
SELECT pg_create_physical_replication_slot('standby1');
Physical / streaming replication (whole-cluster, binary): a standby connects via
primary_conninfo, replays WAL, optionally serves read-only queries (hot standby). Set up
the standby from pg_basebackup -R (writes primary_conninfo + standby.signal).
Logical replication (pg10+) — selective, table-level, cross-version, via publish/subscribe:
-- on the publisher (needs wal_level = logical)
CREATE PUBLICATION pub FOR TABLE orders, customers;
CREATE PUBLICATION pub2 FOR TABLES IN SCHEMA sales; -- (pg15+)
CREATE PUBLICATION pub3 FOR TABLE orders (id, total) WHERE (total > 0); -- column list + row filter (pg15+)
-- on the subscriber
CREATE SUBSCRIPTION sub CONNECTION 'host=pub dbname=app' PUBLICATION pub;
- Replication slots guarantee the primary keeps WAL until a consumer has it — an
abandoned slot can fill the disk; monitor and drop stale slots.
- Synchronous replication: list standbys in
synchronous_standby_names;
synchronous_commit controls the durability/latency trade-off.
- Failover slot sync (pg17+):
sync_replication_slots = on plus synchronized_standby_slots
let logical slots survive a failover to a physical standby (so subscribers don't break).
pg_createsubscriber (pg17+) converts a physical standby into a logical subscriber;
effective_wal_level (pg19+) auto-raises the effective WAL level for logical use.
Streaming setup, slots, logical replication, conflicts, and HA: references/replication.md.
6. Major-version upgrades (pg_upgrade)
# Run as the OS owner, both clusters stopped. --check first (read-only validation).
pg_upgrade --old-datadir=/data/17 --new-datadir=/data/18 \
--old-bindir=/usr/lib/postgresql/17/bin \
--new-bindir=/usr/lib/postgresql/18/bin --check
pg_upgrade … --link # hard-link files: fast, but old cluster becomes unusable
pg_upgrade … --clone # CoW clone (where the FS supports it)
pg_upgrade … --swap # (pg18+) swap data dirs — often the fastest
--link/--clone/--copy/--swap trade speed vs. keeping the old cluster recoverable.
Always have a backup and run --check first. Minor upgrades (e.g. 18.1→18.2) are just a
binary swap + restart — no pg_upgrade. Details: references/backup-recovery.md.
7. Monitoring & observability
-- Who's connected / what's running (kill runaway queries here)
SELECT pid, usename, state, wait_event_type, wait_event,
now() - query_start AS runtime, left(query,60) AS query
FROM pg_stat_activity WHERE state <> 'idle' ORDER BY runtime DESC;
SELECT pg_cancel_backend(pid); -- cancel current query (gentle)
SELECT pg_terminate_backend(pid); -- drop the whole connection (forceful)
-- Per-database health: commits/rollbacks, cache hit ratio, deadlocks, conflicts
SELECT datname, xact_commit, xact_rollback, blks_hit, blks_read, deadlocks
FROM pg_stat_database WHERE datname IS NOT NULL;
-- I/O by backend type / object (pg16+): reads, writes, hits, evictions
SELECT backend_type, object, context, reads, writes, hits FROM pg_stat_io;
-- Long-running maintenance progress
SELECT * FROM pg_stat_progress_vacuum; -- VACUUM (bedrock)
SELECT * FROM pg_stat_progress_basebackup; -- base backup (pg13+)
SELECT * FROM pg_stat_progress_copy; -- COPY (pg14+)
-- Replication lag (primary side) and WAL receiver (standby side)
SELECT application_name, state, sent_lsn, replay_lsn,
write_lag, flush_lag, replay_lag FROM pg_stat_replication;
pg_stat_activity is your live console: states, wait_event, and the cancel/terminate
functions. pg_stat_database gives per-DB throughput and a cache-hit ratio
(blks_hit / (blks_hit + blks_read)). pg_stat_io (pg16+) breaks I/O down by source.
- Progress views:
vacuum (bedrock), create_index/cluster (pg12+), analyze &
basebackup (pg13+), copy (pg14+).
- Logging:
log_destination, logging_collector, log_min_duration_statement (log slow
queries), log_connections/log_disconnections, log_checkpoints (on by default since
pg15), log_lock_waits (on by default pg19+). All log_* are sighup → reload.
- Checkpoints: tune with
checkpoint_timeout, max_wal_size, checkpoint_completion_target;
watch log_checkpoints output / pg_stat_checkpointer (pg17+) for too-frequent checkpoints.
- Connections:
max_connections is a restart setting; the practical fix for "too many
clients" is a pooler (PgBouncer / pgcat / built-in pooling in clients) rather than ever
raising max_connections unbounded. superuser_reserved_connections (and
reserved_connections + pg_use_reserved_connections, pg16+) keep headroom for admins.
Views, columns, logging knobs, and a monitoring playbook: references/monitoring.md.
Quick reference
| Task |
Command / query |
| Show / change a setting |
SHOW x; · ALTER SYSTEM SET x = v; then SELECT pg_reload_conf(); |
| Does a change need restart? |
SELECT name,context,pending_restart FROM pg_settings WHERE name='x'; |
| Reload vs restart |
pg_ctl reload -D $PGDATA · pg_ctl restart -D $PGDATA |
| Create login user / group |
CREATE ROLE u LOGIN PASSWORD '…'; · CREATE ROLE g NOLOGIN; |
| Grant on future objects |
ALTER DEFAULT PRIVILEGES IN SCHEMA s GRANT … TO r; |
| Inspect parsed HBA rules |
TABLE pg_hba_file_rules; (pg10+) |
| Logical dump / restore |
pg_dump -Fc -f a.dump db · pg_restore -d db -j4 a.dump |
| Dump roles + tablespaces |
pg_dumpall --globals-only |
| Physical base backup |
pg_basebackup -D dir -Ft -z -X stream -P |
| Incremental backup (pg17+) |
pg_basebackup --incremental=…/backup_manifest + pg_combinebackup |
| Verify a backup (pg13+) |
pg_verifybackup /backup/base |
| Replication status |
TABLE pg_stat_replication; · TABLE pg_replication_slots; |
| Create publication / subscription |
CREATE PUBLICATION p FOR TABLE t; · CREATE SUBSCRIPTION s …; |
| Live queries / kill one |
TABLE pg_stat_activity; · SELECT pg_terminate_backend(pid); |
| I/O stats (pg16+) |
TABLE pg_stat_io; |
| Upgrade major version |
pg_upgrade --old-* --new-* --check then without --check |
| Current version |
SHOW server_version; · SHOW server_version_num; |
Troubleshooting
- "FATAL: sorry, too many clients already" — at
max_connections. Don't reflexively raise
it (each connection costs memory + a process); add a connection pooler. Check current use:
SELECT count(*), state FROM pg_stat_activity GROUP BY state;
- Config change "didn't take" — it's probably a
postmaster-context setting needing a
restart, or you reloaded but the value is overridden in postgresql.auto.conf. Check
SELECT name,setting,source,pending_restart FROM pg_settings WHERE name='…';.
- Can't connect / "no pg_hba.conf entry" — a rule is missing or a broader rule above it
matched first (and
rejected). Inspect TABLE pg_hba_file_rules; and remember first match
wins. After editing, reload (not restart).
- Password auth fails after enabling SCRAM — passwords stored under
md5 aren't usable by
scram-sha-256 rules. Set password_encryption = scram-sha-256 and have users reset their
password so it's re-hashed.
- Disk filling up / WAL won't recycle — usually an inactive replication slot or a
failing
archive_command. Check SELECT slot_name, active, wal_status FROM pg_replication_slots; and drop abandoned slots with pg_drop_replication_slot('…').
- Standby falling behind — inspect
write_lag/flush_lag/replay_lag in
pg_stat_replication; a long-running query on the standby can pause replay (see
hot_standby_feedback / max_standby_streaming_delay).
pg_dump ≠ full backup — it does not capture roles, tablespaces, or other databases.
Pair it with pg_dumpall --globals-only, or use physical backups for whole-cluster DR.
- A feature errors as unknown syntax / missing view — your server may predate it. Check
SHOW server_version against references/version-features.md
(e.g. pg_stat_io needs pg16, --incremental needs pg17).
References
- references/config.md — GUCs in depth: the three config layers,
ALTER SYSTEM, reload vs restart by context, key memory/WAL/checkpoint/connection/logging
parameters with units, and include directives.
- references/auth-roles.md — roles & attributes,
GRANT/REVOKE,
object privileges, DEFAULT PRIVILEGES, predefined roles, role membership (pg16+ options),
and authentication: pg_hba.conf methods, SCRAM, TLS, pg_ident.conf.
- references/backup-recovery.md — logical (
pg_dump/
pg_dumpall/pg_restore) and physical (pg_basebackup, incremental + pg_combinebackup,
manifests + pg_verifybackup), continuous archiving, PITR, and pg_upgrade.
- references/replication.md — WAL, streaming/physical replication,
replication slots, hot standby, synchronous commit, logical replication (publications/
subscriptions, row filters, column lists), failover slot sync,
pg_createsubscriber.
- references/monitoring.md — the
pg_stat_* views, pg_stat_io,
progress views, replication monitoring, logging settings, checkpoint tuning, and a
triage playbook.
- references/version-features.md — sourced
feature → minimum-PostgreSQL-version map (pg10 → pg19), with how-to-read notes and citations.
Resources
1---2name: postgres-admin3description: PostgreSQL server administration & operations — DBA work on a running cluster. Use when configuring a server (postgresql.conf, ALTER SYSTEM, reload vs restart, GUCs), managing roles & privileges (CREATE ROLE, GRANT/REVOKE, DEFAULT PRIVILEGES, predefined roles, membership), authentication (pg_hba.conf, scram-sha-256, peer/cert/ldap), backups & restore (pg_dump/pg_dumpall/pg_restore, pg_basebackup, incremental backups, PITR), replication & HA (streaming, logical publications/subscriptions, replication slots, failover, synchronous commit), pg_upgrade, or monitoring (pg_stat_activity, pg_stat_io, pg_stat_progress_*, log settings, checkpoints, connections). Server-side ops — for the psql client use `psql`, for SQL & query syntax `postgres-sql`, for query tuning & autovacuum mechanics `postgres-performance`, for contrib modules `postgres-extensions`. Features added in a release are tagged inline like `(pg16+)`; untagged items are bedrock (PostgreSQL 9.x or earlier). Verify with `SHOW server_version`.4---56# PostgreSQL Administration (DBA / Server Operations)78## Overview910This skill covers **operating a PostgreSQL server**: configuring it, securing access,11backing it up, replicating it, upgrading it, and watching it run. It is about the12**server and cluster**, not about writing application SQL.1314**Mental model — the cluster hierarchy:**1516```17PostgreSQL instance (one postmaster, one data directory $PGDATA, one port)18└── cluster = all databases managed by that instance19 ├── global objects: roles/users, tablespaces, replication slots (shared, cluster-wide)20 └── database → schema → object (table/view/sequence/function)21```2223- **One running server** (`postgres`/postmaster) serves **many databases**; roles and24 tablespaces are **cluster-global**, not per-database.25- Configuration lives in **`postgresql.conf`** + **`postgresql.auto.conf`** in `$PGDATA`;26 authentication in **`pg_hba.conf`**; runtime state in the **`pg_stat_*`** views.27- The server is a **long-running daemon**: most admin actions are *reload* (cheap, no28 downtime) or, for a minority of settings, *restart* (brief downtime).29- Write-ahead log (**WAL**) is the foundation of crash recovery, backups (PITR), and30 replication — almost everything operational ties back to it.3132## Related skills (disambiguation)3334| If you need… | Use skill |35|---|---|36| **Server config, roles/auth, backups, replication, upgrades, monitoring** | **postgres-admin** (this skill) |37| The `psql` client, meta-commands (`\d`, `\dt`), scripting the shell | `psql` |38| SQL syntax, DDL/DML, data types, functions, query writing | `postgres-sql` |39| Query tuning, `EXPLAIN`, indexes, **autovacuum mechanics/tuning** | `postgres-performance` |40| contrib extensions (`pg_stat_statements`, `postgis`, `pgcrypto`, …) | `postgres-extensions` |4142> Overlap notes: this skill covers the **server-config / GUC** side of vacuum, checkpoints,43> and WAL (the knobs and what they mean operationally); deep autovacuum/query tuning lives in44> `postgres-performance`. `pg_stat_statements` is a contrib module — monitoring with it is in45> `postgres-extensions`; this skill points you at the built-in `pg_stat_*` views.4647## Version awareness4849PostgreSQL ships **one major version per year** (pg10 = 2017 … **pg18 = 2025 stable**,50**pg19 = 2026 beta**). This skill tags features added in **PostgreSQL 10 or later** inline as51`(pgNN+)` **only where a release note sources it**; anything from the **9.x era or earlier is52bedrock** and left untagged. Full sourced map: [references/version-features.md](references/version-features.md).5354**Check what you're running** (annotations are a hard floor — a `(pg17+)` feature simply does55not exist on pg16):5657```bash58psql -c "SHOW server_version" # e.g. 18.159psql -c "SELECT version()" # full build string60postgres --version # the server binary61pg_config --version # build/devel version62psql -c "SHOW server_version_num" # 180001 → easy to compare numerically63```6465## 1. Server configuration6667Settings are **GUCs** (Grand Unified Configuration). Three layers, last-wins:68`postgresql.conf` → `postgresql.auto.conf` (written by `ALTER SYSTEM`) → per-session `SET`.6970```sql71SHOW shared_buffers; -- one setting72SHOW all; -- everything73SELECT name, setting, unit, context, pending_restart74 FROM pg_settings WHERE name = 'work_mem';75SELECT name, setting, source, sourcefile, sourceline -- where did this value come from?76 FROM pg_settings WHERE source NOT IN ('default','override');7778ALTER SYSTEM SET work_mem = '64MB'; -- persists to postgresql.auto.conf (since 9.4)79ALTER SYSTEM RESET work_mem; -- remove the override80SELECT pg_reload_conf(); -- apply reload-able changes, no downtime81SET work_mem = '128MB'; -- this session only82RESET work_mem; -- back to the configured value83```8485```bash86pg_ctl reload -D $PGDATA # SIGHUP — apply reloadable settings (also: systemctl reload)87pg_ctl restart -D $PGDATA # required for some settings (see below)88```8990**Reload vs restart** — a setting's `context` in `pg_settings` tells you:9192| `context` | How to apply | Examples |93|---|---|---|94| `user` / `superuser` | `SET` in-session, or reload | `work_mem`, `statement_timeout` |95| `sighup` | **reload** (`pg_reload_conf()`) | `log_*`, `autovacuum`, `archive_command`, `checkpoint_timeout`, `max_wal_size` |96| `postmaster` | **restart** | `shared_buffers`, `max_connections`, `listen_addresses`, `port`, `wal_level`, `shared_preload_libraries` |9798After editing, `pending_restart = true` in `pg_settings` flags settings that need a restart.99`ALTER SYSTEM` **cannot** set a few bootstrap params (e.g. it warns on settings that have no100effect post-start). Full GUC tour, key parameters, and units: [references/config.md](references/config.md).101102## 2. Roles & privileges103104Roles are **cluster-global**. A "user" is just a role `WITH LOGIN`.105106```sql107CREATE ROLE app LOGIN PASSWORD 'secret'; -- a login user108CREATE ROLE readonly NOLOGIN; -- a group role109CREATE ROLE deployer LOGIN CREATEDB CREATEROLE;110ALTER ROLE app VALID UNTIL '2027-01-01' CONNECTION LIMIT 20;111ALTER ROLE app SET search_path = app, public; -- per-role GUC default112113GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly; -- existing tables only114ALTER DEFAULT PRIVILEGES IN SCHEMA public -- future tables too115 GRANT SELECT ON TABLES TO readonly;116GRANT readonly TO app; -- role membership117118REVOKE INSERT ON orders FROM app;119```120121- **Privileges don't apply retroactively** — `GRANT … ON ALL TABLES` covers tables that exist122 *now*; use `ALTER DEFAULT PRIVILEGES` for objects created *later* (and it's per-creator).123- **`public` schema hardening (pg15+):** new clusters **no longer grant `CREATE` on `public`124 to `PUBLIC`** (CVE-2018-1058 mitigation). On upgraded clusters, consider125 `REVOKE CREATE ON SCHEMA public FROM PUBLIC` yourself.126- **Predefined roles** grant capabilities without superuser: `pg_monitor`,127 `pg_read_all_settings`, `pg_read_all_stats`, `pg_signal_backend`, `pg_read_all_data` /128 `pg_write_all_data` (pg14+), `pg_database_owner` (pg14+), `pg_checkpoint` (pg15+),129 `pg_use_reserved_connections` (pg16+), `pg_create_subscription` (pg16+), `pg_maintain`130 (pg17+, with the `MAINTAIN` privilege), `pg_signal_autovacuum_worker` (pg18+).131- **Role membership options (pg16+):** `GRANT g TO u WITH INHERIT {TRUE|FALSE}, SET132 {TRUE|FALSE}, ADMIN {TRUE|FALSE}` — membership inheritance is now per-grant, not just the133 member's `INHERIT` attribute. (Pre-16, new memberships always inherited per the member role.)134135Full role attribute / GRANT / object-privilege reference: [references/auth-roles.md](references/auth-roles.md).136137## 3. Authentication (`pg_hba.conf`)138139`pg_hba.conf` is matched **top-to-bottom, first match wins** (no fall-through). Format:140141```142# TYPE DATABASE USER ADDRESS METHOD143local all all peer # Unix socket, OS-user match144host all all 127.0.0.1/32 scram-sha-256 # TCP, localhost145hostssl app app 10.0.0.0/8 scram-sha-256 # require TLS146host all all 0.0.0.0/0 reject # deny the rest147```148149```bash150psql -c "SELECT pg_reload_conf()" # pg_hba changes apply on RELOAD, not restart151psql -c "TABLE pg_hba_file_rules" # see parsed rules + any errors (pg10+)152psql -c "TABLE pg_ident_file_mappings" # parsed pg_ident.conf (pg15+)153```154155- **Methods:** `scram-sha-256` (pg10+, the modern default — set `password_encryption =156 scram-sha-256`, the **default since pg14**), `md5` (legacy, **deprecated pg18+**), `peer`157 (local OS user), `cert` (TLS client cert), `ldap`, `gss`/`sspi`, `radius`, `trust` (no158 auth — dev only), `reject`, and `oauth` (pg18+). `pg_ident.conf` maps OS/external names to159 DB roles for `peer`/`cert`/`gss`/`ldap`.160- Order matters: put **specific rules above general ones**. A leading `trust` line shadows161 everything below it.162163Methods, TLS setup, and `pg_ident.conf` mapping: [references/auth-roles.md](references/auth-roles.md).164165## 4. Backup & recovery166167Two families: **logical** (SQL/dump, portable, per-DB) and **physical** (file/block-level,168whole cluster, enables PITR).169170```bash171# Logical — pg_dump (one database). Custom/dir formats are compressed + allow selective,172# parallel restore; plain text is a .sql script.173pg_dump -Fc -f app.dump app # custom format (restore with pg_restore)174pg_dump -Fd -j4 -f app_dir app # directory format, 4 parallel workers175pg_dump --section=pre-data -f schema.sql app176pg_restore -d app -j4 app.dump # parallel restore177pg_dump -t orders -t 'audit_*' app # selected tables178pg_dumpall --globals-only > globals.sql # roles + tablespaces (NOT in pg_dump)179pg_dumpall -f whole_cluster.sql # every DB + globals (text; non-text formats pg19+)180181# Physical — pg_basebackup (whole cluster, byte-exact; basis for replicas & PITR)182pg_basebackup -D /backup/base -Ft -z -P # tar + gzip, with progress183pg_basebackup -D /backup/base -X stream -c fast # include WAL via a 2nd stream184185# Incremental backup (pg17+) — needs summarize_wal=on; combine to restore186pg_basebackup -D /backup/incr --incremental=/backup/base/backup_manifest187pg_combinebackup /backup/base /backup/incr -o /restore/full # (pg17+)188189# Verify (pg13+): manifests are written automatically by pg_basebackup190pg_verifybackup /backup/base191```192193**Point-in-time recovery (PITR):** restore a base backup, then provide a `restore_command`194and a `recovery_target_*`, drop a `recovery.signal` file, and start the server. The old195`recovery.conf` was **removed in pg12** — recovery settings now live in `postgresql.conf` with196`standby.signal` / `recovery.signal` controlling the mode. Full PITR + upgrade procedures:197[references/backup-recovery.md](references/backup-recovery.md).198199## 5. WAL & replication200201```sql202SHOW wal_level; -- minimal | replica (default) | logical203SHOW synchronous_commit; -- on | remote_apply | remote_write | local | off204SELECT * FROM pg_stat_replication; -- on the primary: connected standbys + lag205SELECT * FROM pg_replication_slots; -- slots (prevent WAL removal until consumed)206SELECT pg_create_physical_replication_slot('standby1');207```208209**Physical / streaming replication** (whole-cluster, binary): a standby connects via210`primary_conninfo`, replays WAL, optionally serves read-only queries (hot standby). Set up211the standby from `pg_basebackup -R` (writes `primary_conninfo` + `standby.signal`).212213**Logical replication (pg10+)** — selective, table-level, cross-version, via publish/subscribe:214215```sql216-- on the publisher (needs wal_level = logical)217CREATE PUBLICATION pub FOR TABLE orders, customers;218CREATE PUBLICATION pub2 FOR TABLES IN SCHEMA sales; -- (pg15+)219CREATE PUBLICATION pub3 FOR TABLE orders (id, total) WHERE (total > 0); -- column list + row filter (pg15+)220221-- on the subscriber222CREATE SUBSCRIPTION sub CONNECTION 'host=pub dbname=app' PUBLICATION pub;223```224225- **Replication slots** guarantee the primary keeps WAL until a consumer has it — an226 abandoned slot can **fill the disk**; monitor and drop stale slots.227- **Synchronous replication:** list standbys in `synchronous_standby_names`;228 `synchronous_commit` controls the durability/latency trade-off.229- **Failover slot sync (pg17+):** `sync_replication_slots = on` plus `synchronized_standby_slots`230 let logical slots survive a failover to a physical standby (so subscribers don't break).231- **`pg_createsubscriber` (pg17+)** converts a physical standby into a logical subscriber;232 **`effective_wal_level` (pg19+)** auto-raises the effective WAL level for logical use.233234Streaming setup, slots, logical replication, conflicts, and HA: [references/replication.md](references/replication.md).235236## 6. Major-version upgrades (`pg_upgrade`)237238```bash239# Run as the OS owner, both clusters stopped. --check first (read-only validation).240pg_upgrade --old-datadir=/data/17 --new-datadir=/data/18 \241 --old-bindir=/usr/lib/postgresql/17/bin \242 --new-bindir=/usr/lib/postgresql/18/bin --check243pg_upgrade … --link # hard-link files: fast, but old cluster becomes unusable244pg_upgrade … --clone # CoW clone (where the FS supports it)245pg_upgrade … --swap # (pg18+) swap data dirs — often the fastest246```247248`--link`/`--clone`/`--copy`/`--swap` trade speed vs. keeping the old cluster recoverable.249Always have a **backup** and run `--check` first. Minor upgrades (e.g. 18.1→18.2) are just a250binary swap + restart — no `pg_upgrade`. Details: [references/backup-recovery.md](references/backup-recovery.md).251252## 7. Monitoring & observability253254```sql255-- Who's connected / what's running (kill runaway queries here)256SELECT pid, usename, state, wait_event_type, wait_event,257 now() - query_start AS runtime, left(query,60) AS query258 FROM pg_stat_activity WHERE state <> 'idle' ORDER BY runtime DESC;259SELECT pg_cancel_backend(pid); -- cancel current query (gentle)260SELECT pg_terminate_backend(pid); -- drop the whole connection (forceful)261262-- Per-database health: commits/rollbacks, cache hit ratio, deadlocks, conflicts263SELECT datname, xact_commit, xact_rollback, blks_hit, blks_read, deadlocks264 FROM pg_stat_database WHERE datname IS NOT NULL;265266-- I/O by backend type / object (pg16+): reads, writes, hits, evictions267SELECT backend_type, object, context, reads, writes, hits FROM pg_stat_io;268269-- Long-running maintenance progress270SELECT * FROM pg_stat_progress_vacuum; -- VACUUM (bedrock)271SELECT * FROM pg_stat_progress_basebackup; -- base backup (pg13+)272SELECT * FROM pg_stat_progress_copy; -- COPY (pg14+)273274-- Replication lag (primary side) and WAL receiver (standby side)275SELECT application_name, state, sent_lsn, replay_lsn,276 write_lag, flush_lag, replay_lag FROM pg_stat_replication;277```278279- **`pg_stat_activity`** is your live console: states, `wait_event`, and the cancel/terminate280 functions. **`pg_stat_database`** gives per-DB throughput and a cache-hit ratio281 (`blks_hit / (blks_hit + blks_read)`). **`pg_stat_io` (pg16+)** breaks I/O down by source.282- **Progress views:** `vacuum` (bedrock), `create_index`/`cluster` (pg12+), `analyze` &283 `basebackup` (pg13+), `copy` (pg14+).284- **Logging:** `log_destination`, `logging_collector`, `log_min_duration_statement` (log slow285 queries), `log_connections`/`log_disconnections`, `log_checkpoints` (on by default since286 pg15), `log_lock_waits` (on by default pg19+). All `log_*` are `sighup` → reload.287- **Checkpoints:** tune with `checkpoint_timeout`, `max_wal_size`, `checkpoint_completion_target`;288 watch `log_checkpoints` output / `pg_stat_checkpointer` (pg17+) for too-frequent checkpoints.289- **Connections:** `max_connections` is a **restart** setting; the practical fix for "too many290 clients" is a **pooler** (PgBouncer / pgcat / built-in pooling in clients) rather than ever291 raising `max_connections` unbounded. `superuser_reserved_connections` (and292 `reserved_connections` + `pg_use_reserved_connections`, pg16+) keep headroom for admins.293294Views, columns, logging knobs, and a monitoring playbook: [references/monitoring.md](references/monitoring.md).295296## Quick reference297298| Task | Command / query |299|---|---|300| Show / change a setting | `SHOW x;` · `ALTER SYSTEM SET x = v;` then `SELECT pg_reload_conf();` |301| Does a change need restart? | `SELECT name,context,pending_restart FROM pg_settings WHERE name='x';` |302| Reload vs restart | `pg_ctl reload -D $PGDATA` · `pg_ctl restart -D $PGDATA` |303| Create login user / group | `CREATE ROLE u LOGIN PASSWORD '…';` · `CREATE ROLE g NOLOGIN;` |304| Grant on future objects | `ALTER DEFAULT PRIVILEGES IN SCHEMA s GRANT … TO r;` |305| Inspect parsed HBA rules | `TABLE pg_hba_file_rules;` (pg10+) |306| Logical dump / restore | `pg_dump -Fc -f a.dump db` · `pg_restore -d db -j4 a.dump` |307| Dump roles + tablespaces | `pg_dumpall --globals-only` |308| Physical base backup | `pg_basebackup -D dir -Ft -z -X stream -P` |309| Incremental backup (pg17+) | `pg_basebackup --incremental=…/backup_manifest` + `pg_combinebackup` |310| Verify a backup (pg13+) | `pg_verifybackup /backup/base` |311| Replication status | `TABLE pg_stat_replication;` · `TABLE pg_replication_slots;` |312| Create publication / subscription | `CREATE PUBLICATION p FOR TABLE t;` · `CREATE SUBSCRIPTION s …;` |313| Live queries / kill one | `TABLE pg_stat_activity;` · `SELECT pg_terminate_backend(pid);` |314| I/O stats (pg16+) | `TABLE pg_stat_io;` |315| Upgrade major version | `pg_upgrade --old-* --new-* --check` then without `--check` |316| Current version | `SHOW server_version;` · `SHOW server_version_num;` |317318## Troubleshooting319320- **"FATAL: sorry, too many clients already"** — at `max_connections`. Don't reflexively raise321 it (each connection costs memory + a process); add a **connection pooler**. Check current use:322 `SELECT count(*), state FROM pg_stat_activity GROUP BY state;`323- **Config change "didn't take"** — it's probably a `postmaster`-context setting needing a324 **restart**, or you reloaded but the value is overridden in `postgresql.auto.conf`. Check325 `SELECT name,setting,source,pending_restart FROM pg_settings WHERE name='…';`.326- **Can't connect / "no pg_hba.conf entry"** — a rule is missing or a broader rule above it327 matched first (and `reject`ed). Inspect `TABLE pg_hba_file_rules;` and remember **first match328 wins**. After editing, **reload** (not restart).329- **Password auth fails after enabling SCRAM** — passwords stored under `md5` aren't usable by330 `scram-sha-256` rules. Set `password_encryption = scram-sha-256` and have users **reset their331 password** so it's re-hashed.332- **Disk filling up / WAL won't recycle** — usually an **inactive replication slot** or a333 failing `archive_command`. Check `SELECT slot_name, active, wal_status FROM334 pg_replication_slots;` and drop abandoned slots with `pg_drop_replication_slot('…')`.335- **Standby falling behind** — inspect `write_lag`/`flush_lag`/`replay_lag` in336 `pg_stat_replication`; a long-running query on the standby can pause replay (see337 `hot_standby_feedback` / `max_standby_streaming_delay`).338- **`pg_dump` ≠ full backup** — it does **not** capture roles, tablespaces, or other databases.339 Pair it with `pg_dumpall --globals-only`, or use physical backups for whole-cluster DR.340- **A feature errors as unknown syntax / missing view** — your server may predate it. Check341 `SHOW server_version` against [references/version-features.md](references/version-features.md)342 (e.g. `pg_stat_io` needs pg16, `--incremental` needs pg17).343344## References345346- [references/config.md](references/config.md) — GUCs in depth: the three config layers,347 `ALTER SYSTEM`, reload vs restart by `context`, key memory/WAL/checkpoint/connection/logging348 parameters with units, and `include` directives.349- [references/auth-roles.md](references/auth-roles.md) — roles & attributes, `GRANT`/`REVOKE`,350 object privileges, `DEFAULT PRIVILEGES`, predefined roles, role membership (pg16+ options),351 and authentication: `pg_hba.conf` methods, SCRAM, TLS, `pg_ident.conf`.352- [references/backup-recovery.md](references/backup-recovery.md) — logical (`pg_dump`/353 `pg_dumpall`/`pg_restore`) and physical (`pg_basebackup`, incremental + `pg_combinebackup`,354 manifests + `pg_verifybackup`), continuous archiving, PITR, and `pg_upgrade`.355- [references/replication.md](references/replication.md) — WAL, streaming/physical replication,356 replication slots, hot standby, synchronous commit, logical replication (publications/357 subscriptions, row filters, column lists), failover slot sync, `pg_createsubscriber`.358- [references/monitoring.md](references/monitoring.md) — the `pg_stat_*` views, `pg_stat_io`,359 progress views, replication monitoring, logging settings, checkpoint tuning, and a360 triage playbook.361- [references/version-features.md](references/version-features.md) — sourced362 feature → minimum-PostgreSQL-version map (pg10 → pg19), with how-to-read notes and citations.363364## Resources365366- **Official docs:** https://www.postgresql.org/docs/current/367- **Server admin:** https://www.postgresql.org/docs/current/admin.html368- **Config (GUCs):** https://www.postgresql.org/docs/current/runtime-config.html369- **Client auth:** https://www.postgresql.org/docs/current/client-authentication.html370- **Backup & restore:** https://www.postgresql.org/docs/current/backup.html371- **High availability:** https://www.postgresql.org/docs/current/high-availability.html372- **Monitoring:** https://www.postgresql.org/docs/current/monitoring-stats.html