PostgreSQL Configuration and Tuning
PostgreSQL performance depends heavily on proper configuration. Default settings are conservative and suited for development, not production workloads.
Config File Locations
| OS | Path |
| Ubuntu | /etc/postgresql/15/main/postgresql.conf |
| CentOS | /var/lib/pgsql-15/data/postgresql.conf |
| macOS | /usr/local/var/postgres/postgresql.conf |
| Windows | C:\Program Files\PostgreSQL\15\data\postgresql.conf |
Related files: pg_hba.conf (authentication), pg_ident.conf (user mapping).
Viewing and Changing Parameters
SHOW config_file;
SHOW shared_buffers;
SELECT current_setting('max_connections');
-- Persistent change (writes to postgresql.auto.conf, does NOT apply immediately)
ALTER SYSTEM SET shared_buffers = '2GB';
-- Then reload or restart:
SELECT pg_reload_conf(); -- for sighup-context params
-- pg_ctl restart -- for postmaster-context params
-- Session-level (temporary)
SET work_mem = '256MB';
-- Reset
ALTER SYSTEM RESET shared_buffers;
Parameter Context Types
internal - cannot change (compile-time) postmaster - requires server restart sighup - re-read config files (no restart) superuser - changeable at runtime by superuser user - changeable at runtime by any user
Memory
| Parameter | Description | Starting Value |
shared_buffers | Main page cache | 25% of RAM |
effective_cache_size | Planner hint for total cache (OS + PG) | 75% of RAM |
work_mem | Per-operation sort/hash memory | 10-64MB |
maintenance_work_mem | VACUUM, CREATE INDEX | 256MB-1GB |
temp_buffers | Temporary table memory | Default |
huge_pages | Use huge pages if available | try |
Memory formula: shared_buffers + (work_mem + temp_buffers) * max_connections must not exceed RAM.
WARNING: Each sort/hash gets its own work_mem, and a single query may have multiple. work_mem * active_connections * operations_per_query must fit in RAM.
WAL and Checkpoints
| Parameter | Description | Starting Value |
max_wal_size | Max WAL between checkpoints | 4-16GB |
checkpoint_completion_target | Spread I/O over checkpoint interval | 0.9 |
wal_compression | Compress WAL records | on |
synchronous_commit | WAL sync on commit | on (safest) |
Query Planner
| Parameter | Description | Starting Value |
random_page_cost | Random page read cost | 1.1-2.0 (SSD) |
effective_io_concurrency | Concurrent I/O requests | 200 (SSD), 500-1000 (NVMe) |
default_statistics_target | Rows sampled for statistics | 100-500 |
jit | JIT compilation | on for analytics |
Connections
| Parameter | Description | Starting Value |
max_connections | Max client connections | 50-200 (use pooler) |
idle_in_transaction_session_timeout | Kill idle transactions | 5-30 min |
statement_timeout | Max query duration | application-specific |
Network Access
# postgresql.conf
listen_addresses = '*'
# pg_hba.conf - allow remote connections
host all all 0.0.0.0/0 scram-sha-256
sudo pg_ctlcluster 15 main restart
Benchmarking with pgbench
# Initialize test database (~1.5M rows)
pgbench -h HOST -p 5432 -U postgres -i -s 15 benchmark
# Run test: 50 clients, 2 threads, report every 60s, run 3 minutes
pgbench -h HOST -p 5432 -U postgres -c 50 -j 2 -P 60 -T 180 benchmark
- pgtune.leopard.in.ua - web calculator based on hardware specs
- pgconfigurator.cybertec.at - advanced configuration generator
- timescaledb-tune - CLI tool for automated tuning
Gotchas
- Default
shared_buffers = 128MB is far too low for production random_page_cost = 4.0 (default) penalizes index scans on SSD - lower to 1.1-2.0 - Increasing
max_connections beyond 200 without a pooler causes context switching overhead effective_cache_size doesn't allocate memory - it's a planner hint - Same parameter appearing multiple times: last value wins in config file
ALTER SYSTEM SET writes to postgresql.auto.conf, overriding postgresql.conf
See Also