PostgreSQL Default Config Itu Buat Server Jadul, Bukan Laptop Kamu
Pertama kali install PostgreSQL, kamu langsung initdb terus jalanin. Works. Tapi lama-lama kamu sadar: query yang harusnya cepet malah ngesot, EXPLAIN ANALYZE nunjukin angka gila, dan setiap kali kamu jalanin test suite yang ada 200 database call, butuh 40 detik.
Masalahnya bukan kodenya. Masalahnya postgresql.conf default itu dikonfigurasi untuk hardware 2005 — RAM 256 MB, disk spinning, dan server yang harus stabil 24/7 tanpa restart. Buat development di laptop modern, setting itu konservatif banget sampai nyakitin produktivitas.
Artikel ini fokus ke konfigurasi PostgreSQL untuk development — bukan production. Bedanya signifikan. Di development, kamu lebih peduli kecepatan dan feedback loop cepat daripada durability data kalau listrik mati.
Cari Dulu postgresql.conf Kamu Di Mana
Sebelum edit apa-apa, cari dulu file config-nya. Ini sering jadi gotcha pertama.
-- Jalanin ini di psql
SHOW config_file;
Output-nya biasanya:
- Linux (apt):
/etc/postgresql/15/main/postgresql.conf - macOS (Homebrew):
/opt/homebrew/var/postgresql@15/postgresql.conf - Docker:
/var/lib/postgresql/data/postgresql.conf
Kalau kamu pakai Docker, cara paling bersih adalah mount file config-nya:
# docker-compose.yml
services:
db:
image: postgres:16
volumes:
- ./postgres.conf:/etc/postgresql/postgresql.conf
- pgdata:/var/lib/postgresql/data
command: postgres -c config_file=/etc/postgresql/postgresql.conf
environment:
POSTGRES_PASSWORD: devpassword
volumes:
pgdata:
Jadi kamu bisa track perubahan config lewat git bareng kodenya. Rapi.
Setting Memory: Ini Yang Paling Ngaruh
PostgreSQL punya beberapa parameter memory yang saling berkaitan. Salah set satu, yang lain ikut kena.
shared_buffers
Ini cache utama PostgreSQL. Default-nya 128MB — kecil banget untuk laptop yang RAM-nya 16 GB.
Rumus umum: 25% dari total RAM. Tapi untuk development, kamu bisa lebih agresif.
# postgresql.conf
shared_buffers = 2GB # kalau RAM kamu 8–16 GB
work_mem
Memori per operasi sorting dan hashing. Ini per-operation, bukan per-connection — jadi hati-hati kalau kamu set terlalu tinggi dan ada banyak concurrent query.
Tapi untuk development lokal yang biasanya cuma satu koneksi aktif:
work_mem = 256MB
Efeknya langsung kerasa di query yang pakai ORDER BY atau GROUP BY di dataset gede — PostgreSQL bakal sort in-memory daripada spill ke disk.
maintenance_work_mem
Dipakai waktu VACUUM, CREATE INDEX, ALTER TABLE. Set lebih tinggi biar operasi ini cepet selesai.
maintenance_work_mem = 512MB
effective_cache_size
Ini bukan alokasi memori — ini hint ke query planner soal seberapa banyak memori yang tersedia untuk OS cache. Set ke sekitar 75% RAM total.
effective_cache_size = 6GB # untuk RAM 8 GB
Setting Durability: Matiin Yang Nggak Perlu di Dev
Ini bagian yang paling kontroversial tapi paling ngaruh ke kecepatan development.
PostgreSQL default-nya sangat defensif soal durability — data harus aman walau ada power failure. Untuk production, itu benar. Untuk development di laptop kamu? Overkill.
# JANGAN pakai ini di production!
fsync = off
synchronous_commit = off
full_page_writes = off
Gotcha penting: fsync = off artinya kalau PostgreSQL crash atau listrik mati, database bisa corrupt. Untuk development ini oke karena kamu bisa dropdb dan rebuild. Tapi jangan pernah deploy config ini ke server manapun.
Kalau kamu paranoid tapi tetap mau lebih cepat, minimal set ini:
synchronous_commit = off
Ini aja udah kasih speedup signifikan untuk write-heavy workload. Data tetap aman dari corruption, cuma ada delay kecil antara commit acknowledgment dan actual write ke disk.
WAL dan Checkpoint: Kurangi Noise di Dev
wal_buffers = 64MB
checkpoint_completion_target = 0.9
min_wal_size = 1GB
max_wal_size = 4GB
checkpoint_completion_target = 0.9 artinya PostgreSQL nyebar write checkpoint sepanjang 90% dari checkpoint interval — lebih smooth, nggak ada burst I/O tiba-tiba yang bikin query kamu kesenggol.
Logging Yang Berguna Buat Debugging
Default logging PostgreSQL hampir nggak kasih info apa-apa. Untuk development, kamu mau tahu query mana yang lambat.
# Log query yang lebih lambat dari 100ms
log_min_duration_statement = 100
# Log semua koneksi dan disconnection
log_connections = on
log_disconnections = on
# Format log yang lebih readable
log_line_prefix = '%t [%p] %u@%d '
# Log detail soal lock waits
log_lock_waits = on
deadlock_timeout = 1s
Dengan log_min_duration_statement = 100, setiap query yang lebih dari 100ms bakal muncul di log. Ini cara paling cepat nemuin N+1 query di ORM kamu tanpa install tool tambahan.
Lihat log-nya:
# Linux
sudo tail -f /var/log/postgresql/postgresql-15-main.log
# macOS Homebrew
tail -f /opt/homebrew/var/log/postgresql@15.log
# Docker
docker compose logs -f db
Connection Settings untuk Local Dev
max_connections = 100
Default 100 biasanya cukup untuk development. Tapi kalau kamu pakai connection pooler kayak PgBouncer atau framework yang suka buka banyak koneksi (looking at you, beberapa ORM Python), naikkin dikit:
max_connections = 200
Satu hal yang sering dilupain: setiap koneksi itu makan work_mem. Kalau kamu set work_mem = 256MB dan ada 100 koneksi aktif semua lagi sorting, itu 25 GB RAM. Jadi kalau kamu naikkin work_mem, pertimbangin juga max_connections-nya.
File Config Lengkap untuk Development
Ini file yang gue pakai di hampir semua project development lokal. Copy, sesuaikan RAM-nya, langsung jalan.
# postgresql-dev.conf
# Optimized for local development — DO NOT use in production
#------------------------------------------------------------------------------
# CONNECTIONS
#------------------------------------------------------------------------------
max_connections = 100
#------------------------------------------------------------------------------
# MEMORY
#------------------------------------------------------------------------------
shared_buffers = 2GB
work_mem = 256MB
maintenance_work_mem = 512MB
effective_cache_size = 6GB
#------------------------------------------------------------------------------
# WAL
#------------------------------------------------------------------------------
wal_buffers = 64MB
min_wal_size = 1GB
max_wal_size = 4GB
checkpoint_completion_target = 0.9
#------------------------------------------------------------------------------
# DURABILITY (dev only!)
#------------------------------------------------------------------------------
fsync = off
synchronous_commit = off
full_page_writes = off
#------------------------------------------------------------------------------
# QUERY PLANNER
#------------------------------------------------------------------------------
random_page_cost = 1.1 # SSD
effective_io_concurrency = 200 # SSD
#------------------------------------------------------------------------------
# LOGGING
#------------------------------------------------------------------------------
log_min_duration_statement = 100
log_connections = on
log_disconnections = on
log_lock_waits = on
deadlock_timeout = 1s
log_line_prefix = '%t [%p] %u@%d '
Dua baris yang mungkin belum familiar:
random_page_cost = 1.1— default-nya 4.0, yang diasumsikan disk spinning. Kalau kamu pakai SSD (hampir pasti), set ke 1.1 biar query planner lebih sering pilih index scan.effective_io_concurrency = 200— kasih tahu PostgreSQL bahwa storage kamu bisa handle banyak I/O concurrent. Ngaruh ke bitmap heap scan.
Cara Apply Config Tanpa Restart
Beberapa parameter bisa di-reload tanpa restart penuh:
-- Di psql, sebagai superuser
SELECT pg_reload_conf();
-- Atau dari command line
pg_ctl reload -D /path/to/data
-- Docker
docker compose exec db pg_ctl reload -D /var/lib/postgresql/data
Tapi parameter kayak shared_buffers dan max_connections butuh restart penuh. Cek dulu apakah parameter yang kamu ubah butuh restart:
SELECT name, setting, unit, context
FROM pg_settings
WHERE name IN (
'shared_buffers',
'work_mem',
'synchronous_commit',
'fsync',
'log_min_duration_statement'
);
Kolom context bakal bilang postmaster (butuh restart), sighup (cukup reload), atau user (bisa diset per-session).
Verifikasi: Pastiin Config Kamu Kena
Jangan assume config udah ke-apply. Verifikasi:
-- Cek nilai aktif saat ini
SHOW shared_buffers;
SHOW work_mem;
SHOW fsync;
-- Atau sekaligus
SELECT name, setting, unit
FROM pg_settings
WHERE name IN ('shared_buffers', 'work_mem', 'fsync', 'synchronous_commit')
ORDER BY name;
Kalau nilainya masih default padahal udah kamu ubah, kemungkinan:
- File yang kamu edit bukan file yang dibaca PostgreSQL — cek lagi
SHOW config_file - Parameter butuh restart, bukan reload
- Ada
ALTER SYSTEMyang override file config — cekpostgresql.auto.conf
Yang Gue Lakuin di Setup Baru
Setiap kali gue setup project baru atau onboarding ke mesin baru, urutannya:
- Buat folder
infra/di root project - Taruh
postgresql-dev.confdi sana - Di
docker-compose.yml, mount file itu dan setcommand: postgres -c config_file=/etc/postgresql/postgresql.conf - Commit ke git — jadi semua dev di tim (atau gue sendiri di mesin lain) dapat config yang sama
- Jalanin test suite sekali sebelum dan sesudah, bandingkan waktunya
Hasilnya? Test suite yang tadinya 40 detik biasanya turun ke 15–20 detik hanya dari konfigurasi PostgreSQL untuk development yang bener. Tanpa ganti satu baris kode pun.
Langkah lanjutan yang worth di-explore: pakai pg_stat_statements extension untuk track query performance over time, dan setup pgBadger kalau kamu mau analisis log yang lebih visual. Tapi itu topik sendiri.