PostgreSQL DBA Scripts

PostgreSQL DBA Scripts – Complete Reference Library | Bright DBA
PostgreSQL β–Έ DBA Scripts
// br8dba.com β€” script library

PostgreSQL DBA Scripts

39 production-ready scripts for monitoring, performance tuning, vacuum, replication, backup, and administration. All scripts are tested on PostgreSQL 14–17. Click any script to expand. Use the search to filter.

PostgreSQL 14–17 39 Scripts 6 Categories Copy-Ready Production Safe
βˆ…
No scripts match your search.
πŸ“Š
Monitoring
Sessions, wait events, blocking, cache, connections
9 scripts
01 Β· Active Sessions Overview
Full session list with state, duration, query, and wait events from pg_stat_activity.
Monitor+
psql
SELECT
    pid,
    usename,
    datname,
    state,
    wait_event_type,
    wait_event,
    now() - xact_start        AS xact_duration,
    now() - query_start       AS query_duration,
    left(query, 100)          AS query_snippet,
    client_addr,
    application_name
FROM pg_stat_activity
WHERE pid != pg_backend_pid()
ORDER BY xact_start NULLS LAST;
πŸ’‘Filter by state: WHERE state = 'active' for running queries, state = 'idle in transaction' for stuck transactions.
02 Β· Long Running Queries
Find queries running longer than N minutes β€” critical for identifying hangs and performance issues.
Monitor+
psql
-- Change '5 minutes' to your threshold
SELECT
    pid,
    usename,
    datname,
    state,
    wait_event_type,
    wait_event,
    now() - query_start   AS query_duration,
    now() - xact_start    AS xact_duration,
    left(query, 200)       AS query
FROM pg_stat_activity
WHERE
    state != 'idle'
    AND (now() - query_start) > interval '5 minutes'
    AND pid != pg_backend_pid()
ORDER BY query_duration DESC;
⚠To kill a long-running query: SELECT pg_cancel_backend(pid); β€” or force-terminate: SELECT pg_terminate_backend(pid);
03 Β· Wait Event Summary
Real-time aggregated view of what all active sessions are waiting for β€” the #1 performance diagnostic.
Monitor+
psql
-- Run this repeatedly every few seconds to see patterns
SELECT
    COALESCE(wait_event_type, 'CPU / Running') AS wait_type,
    COALESCE(wait_event,      'β€”')           AS wait_event,
    COUNT(*)                                   AS sessions,
    STRING_AGG(pid::text, ', ')              AS pids
FROM pg_stat_activity
WHERE
    state != 'idle'
    AND pid != pg_backend_pid()
GROUP BY wait_event_type, wait_event
ORDER BY sessions DESC;
πŸ’‘High IO:DataFileRead β†’ missing index or shared_buffers too small. High Lock:relation β†’ table lock contention. CPU/Running β†’ queries actively executing.
04 Β· Blocking Sessions
Shows which sessions are blocking others β€” who is the blocker, who is blocked, and the queries involved.
Monitor+
psql
SELECT
    blocked.pid                             AS blocked_pid,
    blocked.usename                         AS blocked_user,
    blocked.wait_event_type                 AS blocked_wait_type,
    left(blocked.query, 80)                 AS blocked_query,
    blocking.pid                            AS blocking_pid,
    blocking.usename                        AS blocking_user,
    left(blocking.query, 80)                AS blocking_query,
    now() - blocked.query_start            AS blocked_duration
FROM pg_stat_activity AS blocked
JOIN pg_stat_activity AS blocking
    ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
WHERE cardinality(pg_blocking_pids(blocked.pid)) > 0
ORDER BY blocked_duration DESC;

-- Terminate the blocker (replace PID)
-- SELECT pg_terminate_backend(blocking_pid);
🚨In Oracle: use v$session / v$lock. In PostgreSQL: pg_blocking_pids() is the key function β€” it returns all PIDs blocking the given PID.
05 Β· Database & Table Sizes
Disk usage breakdown β€” database totals, top tables by size including indexes and TOAST.
Monitor+
psql
-- All databases with sizes
SELECT
    datname,
    pg_size_pretty(pg_database_size(datname)) AS db_size
FROM pg_database
ORDER BY pg_database_size(datname) DESC;

-- Top 20 tables by total size (table + indexes + TOAST)
SELECT
    schemaname,
    tablename,
    pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS total_size,
    pg_size_pretty(pg_relation_size(schemaname||'.'||tablename))       AS table_size,
    pg_size_pretty(pg_indexes_size(schemaname||'.'||tablename))       AS index_size
FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC
LIMIT 20;
06 Β· Connection Statistics
Connection usage vs max_connections, breakdown by user, database, and state.
Monitor+
psql
-- Connection usage summary
SELECT
    current_setting('max_connections')::int       AS max_conn,
    COUNT(*)                                       AS used_conn,
    current_setting('max_connections')::int -
        COUNT(*)                                   AS free_conn,
    round(COUNT(*) * 100.0 /
        current_setting('max_connections')::int, 1) AS pct_used
FROM pg_stat_activity;

-- Connections per user and database
SELECT
    usename,
    datname,
    state,
    COUNT(*)   AS conn_count
FROM pg_stat_activity
WHERE pid != pg_backend_pid()
GROUP BY usename, datname, state
ORDER BY conn_count DESC;
⚠Alert if pct_used > 80%. Deploy PgBouncer if consistently high. Each connection consumes ~5–10MB RAM.
07 Β· Buffer Cache Hit Ratio
Per-database and per-table cache hit ratios β€” indicates whether shared_buffers is sized correctly.
Performance+
psql
-- Database-level cache hit ratio
SELECT
    datname,
    blks_hit,
    blks_read,
    round(blks_hit * 100.0 /
        NULLIF(blks_hit + blks_read, 0), 2) AS cache_hit_pct
FROM pg_stat_database
WHERE datname = current_database()
ORDER BY cache_hit_pct;

-- Per-table cache hit (find cold tables)
SELECT
    schemaname,
    relname,
    heap_blks_hit,
    heap_blks_read,
    round(heap_blks_hit * 100.0 /
        NULLIF(heap_blks_hit + heap_blks_read, 0), 1) AS hit_pct
FROM pg_statio_user_tables
WHERE heap_blks_read > 0
ORDER BY heap_blks_read DESC
LIMIT 20;
πŸ’‘Target: cache hit ratio > 99%. If lower, consider increasing shared_buffers (25% of RAM is a good starting point). Reset stats with SELECT pg_stat_reset();
08 Β· XID Age β€” Wraparound Monitor
Critical monitor: tracks transaction ID age per database and table. Alert before approaching 2.1 billion limit.
Critical+
psql
-- XID age per database (alert if > 1.5 billion)
SELECT
    datname,
    age(datfrozenxid)                                          AS xid_age,
    round(age(datfrozenxid)::numeric / 2100000000 * 100, 1) AS pct_toward_limit,
    current_setting('autovacuum_freeze_max_age')              AS freeze_max_age
FROM pg_database
ORDER BY xid_age DESC;

-- Tables with oldest XID (find the freeze bottleneck)
SELECT
    schemaname,
    relname,
    age(relfrozenxid)  AS table_xid_age,
    pg_size_pretty(pg_relation_size(oid)) AS size
FROM pg_class
WHERE relkind = 'r'
ORDER BY age(relfrozenxid) DESC
LIMIT 15;

-- Force freeze on a specific table
-- VACUUM FREEZE VERBOSE mytable;
🚨CRITICAL: If XID age reaches ~2.1 billion without VACUUM FREEZE, PostgreSQL will refuse all connections with "database is not accepting commands to avoid wraparound data loss." Alert at 1.5B, emergency at 1.8B.
09 Β· Top SQL (pg_stat_statements)
Find the heaviest queries by total time, average time, and call count β€” the first stop for query performance analysis.
Performance+
psql
-- Requires: shared_preload_libraries = 'pg_stat_statements'
-- CREATE EXTENSION pg_stat_statements;

-- Top 15 by total execution time
SELECT
    round(total_exec_time::numeric, 2)  AS total_ms,
    calls,
    round(mean_exec_time::numeric, 2)   AS avg_ms,
    round(stddev_exec_time::numeric, 2) AS stddev_ms,
    rows,
    round(total_exec_time / sum(total_exec_time) OVER() * 100, 1) AS pct_total,
    left(query, 100)                     AS query_snippet
FROM pg_stat_statements
WHERE calls > 10
ORDER BY total_exec_time DESC
LIMIT 15;

-- Top by average execution time (worst per-call queries)
SELECT
    round(mean_exec_time::numeric, 2) AS avg_ms,
    calls,
    left(query, 120) AS query
FROM pg_stat_statements
WHERE calls > 5
ORDER BY mean_exec_time DESC
LIMIT 15;
πŸ’‘Reset statistics after tuning: SELECT pg_stat_statements_reset(); β€” Oracle equivalent: V$SQL / V$SQLSTATS.
⚑
Performance
Bloat, indexes, table stats, seq scans
7 scripts
10 Β· Table Bloat Detection
Identify tables with high dead tuple ratios β€” primary candidates for VACUUM or pg_repack.
Maintenance+
psql
SELECT
    schemaname,
    relname                                                          AS table_name,
    n_live_tup,
    n_dead_tup,
    round(n_dead_tup * 100.0 /
        NULLIF(n_live_tup + n_dead_tup, 0), 1)                     AS dead_pct,
    pg_size_pretty(pg_relation_size(schemaname||'.'||relname))     AS table_size,
    last_autovacuum,
    last_vacuum,
    last_autoanalyze
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY dead_pct DESC, n_dead_tup DESC
LIMIT 20;

-- Action: if dead_pct > 10%, run:
-- VACUUM ANALYZE schemaname.table_name;
-- Or for disk reclaim (LOCKS table!): VACUUM FULL table_name;
-- Production safe: pg_repack -d mydb -t table_name
⚠dead_pct > 10–15% = action needed. dead_pct > 30% = urgent. Use pg_repack instead of VACUUM FULL in production to avoid exclusive locks.
11 Β· Index Bloat Detection
Estimate index bloat and find indexes that need REINDEX CONCURRENTLY.
Maintenance+
psql
-- Index sizes and usage
SELECT
    schemaname,
    tablename,
    indexname,
    pg_size_pretty(pg_relation_size(indexrelid))   AS index_size,
    idx_scan,
    idx_tup_read,
    idx_tup_fetch
FROM pg_stat_user_indexes
JOIN pg_index USING (indexrelid)
WHERE pg_relation_size(indexrelid) > 10485760  -- > 10MB
ORDER BY pg_relation_size(indexrelid) DESC
LIMIT 20;

-- Rebuild a bloated index online (no lock)
-- REINDEX INDEX CONCURRENTLY idx_name;
-- Or rebuild all indexes on a table:
-- REINDEX TABLE CONCURRENTLY mytable;
12 Β· Unused Indexes
Find indexes that are never (or rarely) used by queries β€” reducing write overhead and disk space.
Performance+
psql
SELECT
    schemaname,
    tablename,
    indexname,
    idx_scan                                                   AS times_used,
    pg_size_pretty(pg_relation_size(indexrelid))             AS index_size,
    pg_size_pretty(pg_relation_size(indrelid))               AS table_size,
    CASE WHEN indisunique THEN 'UNIQUE' ELSE '' END           AS unique_flag
FROM pg_stat_user_indexes
JOIN pg_index  ON indexrelid = pg_index.indexrelid
WHERE
    idx_scan = 0
    AND NOT indisprimary
    AND NOT indisunique
    AND pg_relation_size(indexrelid) > 1048576  -- > 1MB
ORDER BY pg_relation_size(indexrelid) DESC;

-- DROP unused index (always test first):
-- DROP INDEX CONCURRENTLY idx_name;
⚠Stats reset on server restart. Only act on indexes with zero scans over a long representative period (weeks). Never drop UNIQUE or PRIMARY KEY indexes.
13 Β· Tables with Many Sequential Scans (Missing Indexes)
Tables with high sequential scan counts relative to index scans β€” candidates for index creation.
Performance+
psql
SELECT
    schemaname,
    relname                                                          AS table_name,
    seq_scan,
    idx_scan,
    round(seq_scan * 100.0 /
        NULLIF(seq_scan + idx_scan, 0), 1)                        AS seq_pct,
    seq_tup_read,
    pg_size_pretty(pg_relation_size(schemaname||'.'||relname))     AS table_size,
    n_live_tup
FROM pg_stat_user_tables
WHERE
    seq_scan > 100
    AND pg_relation_size(schemaname||'.'||relname) > 10485760  -- > 10MB
ORDER BY seq_tup_read DESC
LIMIT 20;
πŸ’‘seq_pct > 90% on a large, frequently-queried table = likely missing index. Use EXPLAIN ANALYZE to confirm, then CREATE INDEX CONCURRENTLY to add without locking.
14 Β· Index vs Sequential Scan Ratio
Overall balance of index scans vs sequential scans across the entire database.
Performance+
psql
SELECT
    SUM(seq_scan)                                        AS total_seq_scans,
    SUM(idx_scan)                                        AS total_idx_scans,
    round(SUM(idx_scan) * 100.0 /
        NULLIF(SUM(seq_scan) + SUM(idx_scan), 0), 1)  AS idx_pct,
    SUM(seq_tup_read)                                    AS rows_from_seq,
    SUM(idx_tup_fetch)                                   AS rows_from_idx
FROM pg_stat_user_tables;
πŸ’‘Target: idx_pct > 95% for OLTP workloads. Lower values indicate missing indexes or queries doing full-table scans.
15 Β· Index Usage Statistics
Scan counts and row fetch counts per index β€” identify over and under-utilized indexes.
Performance+
psql
SELECT
    schemaname,
    tablename,
    indexname,
    idx_scan        AS scan_count,
    idx_tup_read    AS rows_read,
    idx_tup_fetch   AS rows_fetched,
    pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC
LIMIT 30;
16 Β· Table Statistics Health Check
Check if table statistics are stale β€” stale stats cause the planner to choose bad execution plans.
Performance+
psql
SELECT
    schemaname,
    relname,
    n_live_tup,
    n_mod_since_analyze                                             AS modified_since_analyze,
    round(n_mod_since_analyze * 100.0 /
        NULLIF(n_live_tup, 0), 1)                                   AS mod_pct,
    last_autoanalyze,
    last_analyze
FROM pg_stat_user_tables
WHERE n_live_tup > 10000
ORDER BY n_mod_since_analyze DESC
LIMIT 20;

-- Force statistics update on a table
-- ANALYZE VERBOSE mytable;
⚠mod_pct > 10% = statistics may be stale. Run ANALYZE. Stale statistics cause the planner to underestimate row counts and choose suboptimal plans.
πŸ”§
Autovacuum
Status, tuning, workers, blockers
5 scripts
17 Β· Autovacuum Configuration Status
Show all autovacuum-related settings currently in effect.
Maintenance+
psql
SELECT name, setting, unit, short_desc
FROM pg_settings
WHERE name LIKE 'autovacuum%'
   OR name LIKE 'vacuum%'
ORDER BY name;

-- Check per-table autovacuum overrides
SELECT
    relname,
    reloptions
FROM pg_class
WHERE reloptions IS NOT NULL
  AND relkind = 'r'
ORDER BY relname;
18 Β· Dead Tuple Report
Comprehensive dead tuple report with estimated autovacuum trigger thresholds per table.
Maintenance+
psql
SELECT
    schemaname,
    relname,
    n_live_tup,
    n_dead_tup,
    round(n_dead_tup * 100.0 /
        NULLIF(n_live_tup + n_dead_tup,0), 1)           AS dead_pct,
    -- autovacuum trigger threshold
    50 + 0.2 * n_live_tup                             AS vacuum_threshold,
    n_dead_tup >
        (50 + 0.2 * n_live_tup)                         AS needs_vacuum,
    last_autovacuum,
    last_vacuum
FROM pg_stat_user_tables
WHERE n_live_tup > 100
ORDER BY n_dead_tup DESC
LIMIT 25;
19 Β· Active Vacuum Workers
Show currently running autovacuum and manual VACUUM processes.
Maintenance+
psql
SELECT
    pid,
    now() - query_start   AS duration,
    query,
    state,
    wait_event_type,
    wait_event
FROM pg_stat_activity
WHERE query LIKE 'autovacuum%'
   OR query ILIKE '%vacuum%'
ORDER BY query_start;

-- Check autovacuum worker count vs max
SELECT
    count(*)                                         AS active_workers,
    current_setting('autovacuum_max_workers')::int   AS max_workers
FROM pg_stat_activity
WHERE query LIKE 'autovacuum:%';
⚠If active_workers = max_workers constantly, autovacuum is overwhelmed. Increase autovacuum_max_workers or reduce autovacuum_vacuum_scale_factor to trigger more frequent but smaller vacuums.
20 Β· Last Vacuum / Analyze Timestamps
When was each table last vacuumed and analyzed β€” identify tables being neglected by autovacuum.
Maintenance+
psql
SELECT
    schemaname,
    relname,
    last_vacuum,
    last_autovacuum,
    last_analyze,
    last_autoanalyze,
    vacuum_count,
    autovacuum_count,
    n_live_tup,
    n_dead_tup
FROM pg_stat_user_tables
ORDER BY
    GREATEST(last_vacuum, last_autovacuum) NULLS FIRST
LIMIT 20;
πŸ’‘Tables with last_autovacuum = NULL or very old despite high n_dead_tup are not being vacuumed β€” check for long-running transactions blocking autovacuum (see script 21).
21 Β· Autovacuum Blockers (Idle-in-Transaction)
Find sessions whose open transactions are preventing autovacuum from advancing the XID horizon.
Maintenance+
psql
-- Sessions blocking autovacuum's oldest xmin
SELECT
    pid,
    usename,
    datname,
    state,
    backend_xmin,
    now() - xact_start   AS open_transaction_age,
    left(query, 100)     AS last_query
FROM pg_stat_activity
WHERE
    state IN (
        'idle in transaction',
        'idle in transaction (aborted)'
    )
ORDER BY xact_start NULLS LAST;

-- Overall oldest xmin in the cluster
SELECT
    min(backend_xmin) AS oldest_xmin,
    now() - min(xact_start) AS oldest_txn_age
FROM pg_stat_activity
WHERE backend_xmin IS NOT NULL;
🚨Terminate long idle-in-transaction sessions: SELECT pg_terminate_backend(pid); β€” Prevent recurrence: set idle_in_transaction_session_timeout = '10min' in postgresql.conf.
πŸ”
Replication
Streaming replication status, lag, WAL, archiver, slots
6 scripts
22 Β· Replication Status (Primary)
Full replication status from primary β€” connected standbys, sync state, and sent/replayed LSN positions.
HA+
psql β€” run on PRIMARY
SELECT
    application_name,
    client_addr,
    state,
    sync_state,
    sent_lsn,
    write_lsn,
    flush_lsn,
    replay_lsn,
    write_lag,
    flush_lag,
    replay_lag,
    pg_size_pretty(sent_lsn - replay_lsn) AS lag_bytes
FROM pg_stat_replication
ORDER BY application_name;
πŸ’‘sync_state values: async (default), sync (synchronous), potential (will become sync if current sync fails), quorum.
23 Β· Replication Lag Monitor
Lag in bytes and seconds β€” run on both primary (to see all standbys) and on each standby.
HA+
psql
-- On PRIMARY: lag per standby
SELECT
    application_name,
    pg_size_pretty(pg_wal_lsn_diff(
        pg_current_wal_lsn(), replay_lsn))   AS replay_lag_bytes,
    replay_lag                                AS replay_lag_time,
    flush_lag,
    write_lag
FROM pg_stat_replication;

-- On STANDBY: lag from this replica's perspective
SELECT
    now() - pg_last_xact_replay_timestamp()  AS replication_delay,
    pg_last_wal_receive_lsn()                 AS received_lsn,
    pg_last_wal_replay_lsn()                  AS replayed_lsn,
    pg_is_in_recovery()                       AS is_standby;
⚠Alert thresholds: <1s = good, 1–30s = acceptable, >60s = investigate. High lag = standby I/O bottleneck, network issue, or standby under heavy query load.
24 Β· Replication Slot Monitor
All replication slots with WAL retained and active status. Critical to prevent pg_wal directory filling up.
HA+
psql
SELECT
    slot_name,
    slot_type,
    database,
    active,
    active_pid,
    pg_size_pretty(pg_wal_lsn_diff(
        pg_current_wal_lsn(),
        COALESCE(confirmed_flush_lsn, restart_lsn))) AS wal_retained,
    restart_lsn,
    confirmed_flush_lsn
FROM pg_replication_slots
ORDER BY pg_wal_lsn_diff(
    pg_current_wal_lsn(),
    COALESCE(confirmed_flush_lsn, restart_lsn)) DESC;

-- Drop a stale/unused slot (DANGEROUS if consumer still needs it)
-- SELECT pg_drop_replication_slot('slot_name');
🚨An inactive slot retaining >10GB of WAL is a disk-filling risk. Alert when wal_retained > 5GB. Set max_slot_wal_keep_size = 10GB in postgresql.conf as a safety valve (PG 13+).
25 Β· WAL Generation Statistics
WAL write volume, sync calls, and checkpoint statistics for write amplification analysis.
HA+
psql β€” PG 14+
-- WAL generation stats (PostgreSQL 14+)
SELECT
    wal_records,
    wal_fpi                                          AS full_page_images,
    pg_size_pretty(wal_bytes)                        AS wal_bytes,
    wal_buffers_full,
    wal_write,
    wal_sync,
    round(wal_write_time / 1000, 2)                AS write_time_sec,
    round(wal_sync_time  / 1000, 2)                AS sync_time_sec
FROM pg_stat_wal;

-- Checkpoint stats
SELECT
    checkpoints_timed,
    checkpoints_req,
    buffers_checkpoint,
    buffers_clean,
    buffers_backend,
    round(checkpoint_write_time / 1000, 2)         AS write_sec,
    round(checkpoint_sync_time  / 1000, 2)         AS sync_sec
FROM pg_stat_bgwriter;
26 Β· WAL Archiver Status
Check if WAL archiving is working correctly β€” failed archives break PITR capability.
HA+
psql
SELECT
    archived_count,
    last_archived_wal,
    last_archived_time,
    failed_count,
    last_failed_wal,
    last_failed_time,
    now() - last_archived_time   AS time_since_last_archive
FROM pg_stat_archiver;

-- Verify archive_mode and archive_command
SHOW archive_mode;
SHOW archive_command;

-- Force a WAL switch to test archive_command
-- SELECT pg_switch_wal();
🚨If failed_count > 0 or time_since_last_archive > 10 minutes, your PITR backup is broken. Check archive_command permissions and disk space immediately.
27 Β· Recovery / Standby Status
Check if this server is in recovery (standby) or is a primary, and current LSN positions.
HA+
psql
SELECT
    pg_is_in_recovery()                        AS is_standby,
    pg_last_wal_receive_lsn()                  AS received_lsn,
    pg_last_wal_replay_lsn()                   AS replayed_lsn,
    pg_last_xact_replay_timestamp()            AS last_replay_time,
    now() - pg_last_xact_replay_timestamp()    AS replay_lag;

-- Promote standby to primary (PostgreSQL 12+)
-- SELECT pg_promote();
-- Or: pg_ctl promote -D $PGDATA
πŸ’Ύ
Backup
pg_basebackup, pg_dump, PITR setup, verification
4 scripts
28 Β· pg_basebackup Commands
Ready-to-use pg_basebackup commands for various scenarios β€” backup to local dir, with WAL, background mode.
Backup+
shell
# Standard backup (tar format, stream WAL, show progress)
pg_basebackup \
    -U postgres \
    -D /pgBackup/pgsql17/backup/base_$(date +%d%b%Y) \
    -Ft -Xs -P -v \
    > /pgBackup/pgsql17/backup/base_$(date +%d%b%Y).log 2>&1

# Run in background with nohup
nohup pg_basebackup \
    -U postgres \
    -D /pgBackup/pgsql17/backup/base_$(date +%d%b%Y) \
    -Ft -Xs -P -v \
    > /pgBackup/pgsql17/backup/base_$(date +%d%b%Y).log 2>&1 &

# Remote backup (from standby or DBA host)
pg_basebackup \
    -h 192.168.1.51 -p 5432 \
    -U repl_user \
    -D /pgBackup/base_$(date +%d%b%Y) \
    -Ft -Xs -P -v -R

# -R flag auto-creates standby.signal + postgresql.auto.conf
# for streaming replication setup
πŸ’‘Flags: -Ft = tar format, -Xs = stream WAL during backup, -P = progress, -v = verbose, -R = write recovery config (for standby setup).
29 Β· pg_dump / pg_restore Commands
Complete pg_dump and pg_restore command reference for database, schema, and table-level operations.
Backup+
shell
# Dump entire database (custom format β€” recommended)
pg_dump -U postgres -d mydb -Fc -v \
    -f /backup/mydb_$(date +%d%b%Y).dmp

# Dump schema only (no data)
pg_dump -U postgres -d mydb --schema-only \
    -f /backup/mydb_schema.sql

# Dump data only
pg_dump -U postgres -d mydb --data-only \
    -Fc -f /backup/mydb_data.dmp

# Dump specific table
pg_dump -U postgres -d mydb \
    -t public.orders -Fc -v \
    -f /backup/orders_$(date +%d%b%Y).dmp

# Dump all databases + global objects
pg_dumpall -U postgres -f /backup/all_$(date +%d%b%Y).sql

# Restore full database
pg_restore -U postgres -d mydb -v \
    /backup/mydb_$(date +%d%b%Y).dmp

# Restore single table into existing DB
pg_restore -U postgres -d mydb \
    -t public.orders -v /backup/orders.dmp

# Restore with parallel workers (faster)
pg_restore -U postgres -d mydb -j 4 -v \
    /backup/mydb.dmp
30 Β· PITR Configuration Checklist
Step-by-step postgresql.conf settings and signal file creation for Point-in-Time Recovery.
Backup+
shell + SQL
# 1. On SOURCE β€” enable archiving (postgresql.conf) wal_level = replica archive_mode = on archive_command = 'cp %p /pgArch/pgsql17/arch/%f' # 2. On TARGET β€” restore base backup then configure recovery # Edit postgresql.conf on target: restore_command = 'cp /pgbackup/archive/%f %p' recovery_target_time = '2025-10-12 22:05:00' recovery_target_action = 'pause' # or 'promote' # 3. Create recovery.signal (PostgreSQL 12+) touch $PGDATA/recovery.signal chmod 600 $PGDATA/recovery.signal # 4. Start PostgreSQL β€” it will enter recovery mode systemctl start postgresql-17 # 5. Monitor recovery in logs tail -f $PGDATA/log/postgresql-*.log # 6. After pausing at target time, validate data, then promote
psql β€” after recovery pauses
-- Check if paused at target time
SELECT pg_is_in_recovery(), pg_last_xact_replay_timestamp();

-- Validate your data is as expected
SELECT * FROM myschema.mytable LIMIT 10;

-- Promote to full primary
SELECT pg_wal_replay_resume();

-- Verify promotion complete
SELECT pg_is_in_recovery();  -- should return 'f'
31 Β· Backup Verification
Verify integrity of a pg_basebackup using pg_verifybackup and check backup_manifest.
Backup+
shell β€” PG 13+
# Verify backup integrity using backup_manifest pg_verifybackup /pgBackup/pgsql17/backup/base_12OCT2025 # Skip WAL verification (if WAL is not available) pg_verifybackup --no-parse-wal \ /pgBackup/pgsql17/backup/base_12OCT2025 # Check backup_manifest exists and is readable ls -la /pgBackup/pgsql17/backup/base_12OCT2025/backup_manifest cat /pgBackup/pgsql17/backup/base_12OCT2025/backup_manifest | head -5 # Disk usage of backup du -sh /pgBackup/pgsql17/backup/base_12OCT2025
πŸ’‘Always verify backups. An untested backup is not a backup. Schedule monthly restore drills in a test environment.
πŸ› οΈ
Administration
Users, locks, partitions, config, housekeeping
8 scripts
32 Β· User & Role Management
Common user and role management commands β€” create, grant, revoke, list, and drop roles.
Admin+
psql
-- List all roles and their attributes \du -- Or detailed: SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolreplication, rolconnlimit, rolvaliduntil FROM pg_roles ORDER BY rolname; -- Create application user CREATE ROLE app_user WITH LOGIN PASSWORD 'StrongP@ss123' CONNECTION LIMIT 50 VALID UNTIL '2026-12-31'; -- Grant database and schema access GRANT CONNECT ON DATABASE mydb TO app_user; GRANT USAGE ON SCHEMA public TO app_user; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user; GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_user; -- Create read-only role CREATE ROLE readonly_role; GRANT CONNECT ON DATABASE mydb TO readonly_role; GRANT USAGE ON SCHEMA public TO readonly_role; GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_role; -- Change password ALTER ROLE app_user WITH PASSWORD 'NewP@ss456'; -- Drop user (must own no objects) REASSIGN OWNED BY app_user TO postgres; DROP OWNED BY app_user; DROP ROLE app_user;
33 Β· Tablespace Information
List tablespaces, their locations, sizes, and which databases use them.
Admin+
psql
-- List all tablespaces with size
SELECT
    spcname                                          AS tablespace_name,
    pg_catalog.pg_tablespace_location(oid)           AS location,
    pg_catalog.pg_get_userbyid(spcowner)             AS owner,
    pg_size_pretty(pg_tablespace_size(spcname))     AS size
FROM pg_tablespace
ORDER BY spcname;

-- Which databases use each tablespace?
SELECT
    t.spcname    AS tablespace,
    d.datname    AS database
FROM pg_database d
JOIN pg_tablespace t ON d.dattablespace = t.oid
ORDER BY t.spcname;

-- Symbolic links in pg_tblspc directory
-- ls -la $PGDATA/pg_tblspc/
34 Β· Lock Monitor
Active locks with relation names, modes, and whether they are granted or waiting.
Monitor+
psql
SELECT
    pid,
    locktype,
    COALESCE(relation::regclass::text, 'β€”') AS relation,
    mode,
    granted,
    fastpath,
    pg_stat_activity.state,
    pg_stat_activity.wait_event_type,
    pg_stat_activity.wait_event,
    left(pg_stat_activity.query, 60)          AS query
FROM pg_locks
JOIN pg_stat_activity USING (pid)
WHERE
    locktype != 'virtualxid'
    AND NOT fastpath
ORDER BY granted, pid;
πŸ’‘granted = false means the lock is waiting (blocked). Match with script 04 (Blocking Sessions) to find the root cause.
35 Β· Partition Information
List all partition tables, their partition keys, partition count, and sizes.
Admin+
psql
-- List all partitioned tables and their partition count SELECT parent.relname AS parent_table, parent.relkind, COUNT(child.relname) AS partition_count, pg_size_pretty(SUM(pg_relation_size(child.oid))) AS total_partitions_size FROM pg_class parent JOIN pg_inherits ON pg_inherits.inhparent = parent.oid JOIN pg_class child ON pg_inherits.inhrelid = child.oid WHERE parent.relkind = 'p' GROUP BY parent.relname, parent.relkind ORDER BY parent.relname; -- List partitions of a specific table SELECT child.relname AS partition_name, pg_get_expr(child.relpartbound, child.oid) AS partition_bound, pg_size_pretty(pg_relation_size(child.oid)) AS size FROM pg_class parent JOIN pg_inherits ON pg_inherits.inhparent = parent.oid JOIN pg_class child ON pg_inherits.inhrelid = child.oid WHERE parent.relname = 'orders' -- change table name ORDER BY child.relname;
36 Β· Configuration Health Check
Show key performance parameters and compare current values against recommended best practices.
Admin+
psql
-- Key performance parameters SELECT name, setting, unit, source FROM pg_settings WHERE name IN ( 'shared_buffers', 'work_mem', 'maintenance_work_mem', 'effective_cache_size', 'max_connections', 'wal_level', 'archive_mode', 'checkpoint_timeout', 'max_wal_size', 'checkpoint_completion_target', 'synchronous_commit', 'autovacuum_max_workers', 'autovacuum_vacuum_scale_factor', 'autovacuum_vacuum_cost_limit', 'random_page_cost', 'jit', 'wal_compression', 'max_parallel_workers' ) ORDER BY name; -- Show parameters that differ from defaults SELECT name, setting, boot_val AS default_val, source FROM pg_settings WHERE setting != boot_val AND source != 'default' ORDER BY name;
37 Β· Kill / Cleanup Idle Sessions
Scripts to cancel or terminate sessions by state, age, or user β€” use carefully in production.
Maintenance+
psql
-- Cancel (soft) all queries running > 10 minutes SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > interval '10 minutes' AND pid != pg_backend_pid(); -- Terminate idle-in-transaction sessions older than 30 min SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle in transaction' AND now() - xact_start > interval '30 minutes' AND pid != pg_backend_pid(); -- Terminate all idle connections (not running anything) SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle' AND now() - state_change > interval '1 hour' AND usename != 'replication' AND pid != pg_backend_pid();
🚨Always preview with SELECT pid, state, query... first before executing the terminate. pg_cancel_backend is gentler (sends SIGINT). pg_terminate_backend sends SIGTERM and disconnects the client.
38 Β· Object Sizes by Schema
Space usage breakdown by schema β€” tables, indexes, total. Useful for capacity planning.
Admin+
psql
SELECT schemaname, COUNT(tablename) AS table_count, pg_size_pretty(SUM(pg_relation_size(schemaname||'.'||tablename))) AS tables_size, pg_size_pretty(SUM(pg_indexes_size(schemaname||'.'||tablename))) AS indexes_size, pg_size_pretty(SUM(pg_total_relation_size(schemaname||'.'||tablename))) AS total_size FROM pg_tables WHERE schemaname NOT IN ('pg_catalog', 'information_schema') GROUP BY schemaname ORDER BY SUM(pg_total_relation_size(schemaname||'.'||tablename)) DESC;
39 Β· Extensions & PostgreSQL Version
Installed extensions with versions, and full PostgreSQL version information.
Admin+
psql
-- PostgreSQL version SELECT version(); SHOW server_version; -- Installed extensions SELECT extname AS extension, extversion AS version, nspname AS schema, extrelocatable AS relocatable FROM pg_extension JOIN pg_namespace ON extnamespace = pg_namespace.oid ORDER BY extname; -- Available extensions (can be installed) SELECT name, default_version, comment FROM pg_available_extensions WHERE installed_version IS NULL ORDER BY name; -- Cluster uptime SELECT pg_postmaster_start_time() AS started_at, now() - pg_postmaster_start_time() AS uptime;
Rajasekhar Amudala
Senior Oracle DBA | 16+ Years | RAC Β· Exadata Β· PostgreSQL Β· Performance Tuning
⚠ CAUTION: Scripts provided for educational use. Test in your environment before using in production. Use at your own risk.