← Kembali ke Blog

PostgreSQL di Docker: Setup Produksi, Backup, dan Performance Tuning

Kebanyakan tutorial PostgreSQL di Docker berhenti di docker run postgres. Itu kasih kamu database, tapi bukan yang kamu percaya dengan data production. Nggak ada persistent volume, nggak ada backup, config default dengan shared buffers 128MB, dan connection limit cuma satu.

Artikel ini menjelaskan cara setup PostgreSQL di Docker dengan config yang beneran kuat di bawah load: persistent storage, backup otomatis, connection pooling, dan performance tuning berdasarkan resource server kamu.

Prerequisites

  • Docker Engine 24+ dan Docker Compose v2
  • Server Linux dengan minimal 2GB RAM (4GB+ direkomendasikan untuk production)
  • Cukup disk space buat database dan backup

Langkah 1: Struktur Project

mkdir -p pg-docker && cd pg-docker
mkdir -p data backups config

Struktur:

pg-docker/
  docker-compose.yml
  config/
    postgresql.conf    # performance tuning
    pg_hba.conf        # aturan autentikasi
  data/                # penyimpanan database persisten
  backups/             # output backup otomatis

Langkah 2: Tulis File Docker Compose

Buat docker-compose.yml:

services:
  postgres:
    image: postgres:18.4-alpine
    container_name: postgres
    restart: unless-stopped
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: changeme-strong-password-here
      POSTGRES_DB: myapp
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - ./data:/var/lib/postgresql/data
      - ./config/postgresql.conf:/etc/postgresql/postgresql.conf:ro
      - ./config/pg_hba.conf:/etc/postgresql/pg_hba.conf:ro
      - ./backups:/backups
    command: postgres -c config_file=/etc/postgresql/postgresql.conf -c hba_file=/etc/postgresql/pg_hba.conf
    shm_size: '256mb'

Yang perlu diperhatikan:

  • PGDATA geser direktori data ke subdirektori. Ini mencegah PostgreSQL menolak inisialisasi kalau volume tidak kosong.
  • Override command bilang PostgreSQL buat pake config custom kita alih-alih default.
  • shm_size atur limit shared memory. PostgreSQL pakai shared memory buat buffer pool. Default 64MB terlalu kecil buat apa pun selain testing.
  • Mount volume backups bikin backup script bisa diakses di dalam container.

Langkah 3: Tulis Config PostgreSQL

Buat config/postgresql.conf. Ini config yang sudah di-tuning untuk server dengan 4GB RAM:

# Pengaturan koneksi
listen_addresses = '*'
max_connections = 100

# Pengaturan memory (di-tuning untuk server 4GB RAM)
shared_buffers = 1GB
effective_cache_size = 3GB
work_mem = 16MB
maintenance_work_mem = 512MB

# Pengaturan WAL
wal_buffers = 64MB
checkpoint_completion_target = 0.9
wal_compression = lz4

# Perencanaan query
random_page_cost = 1.1
effective_io_concurrency = 200

# Logging
log_destination = 'stderr'
logging_collector = on
log_directory = 'log'
log_filename = 'postgresql-%Y-%m-%d.log'
log_rotation_age = 1d
log_rotation_size = 100MB
log_min_duration_statement = 1000
log_checkpoints = on
log_connections = on
log_disconnections = on

# Autovacuum
autovacuum = on
autovacuum_max_workers = 3
autovacuum_naptime = 1min

Cara tune ini untuk server kamu:

Setting Rumus Contoh (4GB RAM)
shared_buffers 25% RAM 1GB
effective_cache_size 75% RAM 3GB
work_mem RAM / max_connections / 4 16MB (4096/100/4)
maintenance_work_mem 5-10% RAM 512MB

Untuk server 8GB, gandakan shared_buffers jadi 2GB, effective_cache_size jadi 6GB, dan pertahankan work_mem proporsional.

Langkah 4: Tulis Config Autentikasi

Buat config/pg_hba.conf:

# TYPE  DATABASE  USER      ADDRESS         METHOD
local   all       all                     scram-sha-256
host    all       all       127.0.0.1/32    scram-sha-256
host    all       all       ::1/128         scram-sha-256
host    myapp     appuser   172.16.0.0/12   scram-sha-256
host    all       all       0.0.0.0/0       reject

Config ini:

  • Mewajibkan autentikasi password (scram-sha-256) untuk semua koneksi
  • Memperbolehkan user app dari Docker network (172.16.0.0/12 adalah range default)
  • Menolak semua koneksi remote lainnya

Sesuaikan range CIDR kalau Docker network-mu pakai subnet yang berbeda. Cek pakai docker network inspect <network_name>.

Langkah 5: Jalankan PostgreSQL

docker compose up -d

Pastikan jalan:

docker compose ps
docker compose logs postgres | tail -5

Log output yang diharapkan:

LOG:  starting PostgreSQL 18.4 on x86_64-pc-linux-musl
LOG:  listening on IPv4 address "0.0.0.0", port 5432
LOG:  database system is ready to accept connections

Test koneksi:

docker compose exec postgres psql -U appuser -d myapp -c "SELECT version();"

Kamu harusnya lihat versi PostgreSQL dan info sistem.

Langkah 6: Bikin Schema Berguna

Connect dan bikin test table:

docker compose exec postgres psql -U appuser -d myapp
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    name VARCHAR(255) NOT NULL,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_users_email ON users (email);

INSERT INTO users (email, name) VALUES
    ('[email protected]', 'Alice'),
    ('[email protected]', 'Bob');

SELECT * FROM users;

Ketik q buat keluar dari psql.

Langkah 7: Setup Backup Otomatis

Buat backups/backup.sh:

#!/bin/bash
set -euo pipefail

BACKUP_DIR="/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/myapp_$TIMESTAMP.sql.gz"

# Jalankan pg_dump dan kompres
pg_dump -U appuser -d myapp --no-owner --no-privileges | gzip > "$BACKUP_FILE"

# Hapus backup lebih tua dari 7 hari
find "$BACKUP_DIR" -name "myapp_*.sql.gz" -mtime +7 -delete

echo "Backup selesai: $BACKUP_FILE"

Buat executable dan test:

chmod +x backups/backup.sh
docker compose exec postgres bash /backups/backup.sh

Pastikan backup terbuat:

ls -la backups/

Otomasi Backup dengan Cron

Jalankan backup harian jam 2 pagi pakai crontab host:

crontab -e

Tambah:

0 2 * * * cd /path/to/pg-docker && docker compose exec -T postgres bash /backups/backup.sh >> /var/log/pg-backup.log 2>&1

Flag -T mengalokasikan pseudo-TTY tanpa stdin, yang mencegah cron hanging.

Restore dari Backup

# Lihat backup yang tersedia
ls backups/

# Restore backup tertentu
gunzip -c backups/myapp_20260617_020000.sql.gz | docker compose exec -T postgres psql -U appuser -d myapp

# Atau restore ke database baru
docker compose exec postgres psql -U appuser -c "CREATE DATABASE myapp_restored;"
gunzip -c backups/myapp_20260617_020000.sql.gz | docker compose exec -T postgres psql -U appuser -d myapp_restored

Langkah 8: Tambah pg_stat_statements buat Monitoring

pg_stat_statements lacak performa query. Tambah ke config kamu.

Update config/postgresql.conf:

shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.track = all

Restart PostgreSQL:

docker compose restart postgres

Aktifkan extension:

docker compose exec postgres psql -U appuser -d myapp -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;"

Cek query lambat:

SELECT query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;

Langkah 9: Connection Pooling dengan PgBouncer

Kalau aplikasi kamu buka banyak koneksi singkat, connection pooler cegah PostgreSQL buang resource buat connection overhead.

Tambah PgBouncer ke docker-compose.yml:

services:
  postgres:
    image: postgres:18.4-alpine
    container_name: postgres
    restart: unless-stopped
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: changeme-strong-password-here
      POSTGRES_DB: myapp
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - ./data:/var/lib/postgresql/data
      - ./config/postgresql.conf:/etc/postgresql/postgresql.conf:ro
      - ./config/pg_hba.conf:/etc/postgresql/pg_hba.conf:ro
    command: postgres -c config_file=/etc/postgresql/postgresql.conf -c hba_file=/etc/postgresql/pg_hba.conf
    shm_size: '256mb'
    networks:
      - backend

  pgbouncer:
    image: edoburu/pgbouncer:1.23.1
    container_name: pgbouncer
    restart: unless-stopped
    ports:
      - "6432:6432"
    environment:
      DATABASE_URL: postgres://appuser:***@postgres:5432/myapp
      POOL_MODE: transaction
      MAX_CLIENT_CONN: 1000
      DEFAULT_POOL_SIZE: 20
    depends_on:
      - postgres
    networks:
      - backend

networks:
  backend:
    driver: bridge

Setting PgBouncer yang penting:

  • POOL_MODE: transaction kembalikan koneksi ke pool setiap transaksi alih-alih setiap sesi. Ini bikin 20 koneksi pool bisa serve ratusan client.
  • MAX_CLIENT_CONN adalah maksimum koneksi client yang diterima PgBouncer.
  • DEFAULT_POOL_SIZE adalah berapa koneksi PgBouncer buka ke PostgreSQL per pasang user/database.

Aplikasi kamu connect ke PgBouncer di port 6432 alih-alih PostgreSQL di 5432. Sisanya tetap sama.

Masalah yang Sering Muncul

PostgreSQL menolak start dengan "data directory has wrong ownership". Direktori host (./data) punya permission yang salah. Perbaiki:

sudo chown -R 999:999 ./data

UID 999 adalah user postgres default di image berbasis Alpine.

"FATAL: could not create shared memory segment" shm_size di Docker Compose terlalu kecil buat setting shared_buffers kamu. Samakan: kalau shared_buffers 1GB, shm_size minimal 1GB.

Backup ukurannya besar. pg_dump tanpa kompresi hasilin file besar. Script backup di atas pakai gzip. Untuk kompresi lebih baik, pakai pg_dump --format=custom (hasilin file .dump yang bisa dibaca pg_restore):

pg_dump -U appuser -d myapp --format=custom --file=/backups/myapp_$TIMESTAMP.dump

Koneksi ditolak dari container aplikasi. Container app-mu belum di jaringan Docker yang sama dengan PostgreSQL. Tambah networks: [backend] ke kedua service, atau pakai host network buat setup yang lebih simpel.

Query lambat setelah pindah ke Docker. Cek shared_buffers dan effective_cache_size. Config default PostgreSQL asumsi jalan di bare metal dengan akses penuh ke RAM. Di Docker, kamu harus set ini secara eksplisit berdasarkan memory limit container.

Langkah Selanjutnya

  • Tambah dashboard Grafana + pg_stat_statements buat visualisasi performa query
  • Setup WAL archiving buat point-in-time recovery
  • Pakai pg_basebackup buat full physical backup (restore lebih cepat dari pg_dump buat database besar)
  • Pertimbangkan pgvector kalau kamu butuh vector search buat workload AI/ML

Referensi

Butuh Bantuan Implementasi?

Saya membantu tim mendesain dan membangun infrastruktur cloud scalable, pipeline DevOps, dan sistem production-grade.

Konsultasi Gratis