Header LogoCygne Noir
Back to Home
Database

High Availability PostgreSQL

Zainun Kamal
8/24/2026
1 views

Introduction

Setup standalone PostgreSQL memiliki risiko Single Point of Failure (SPOF) di mana downtime pada node utama menghentikan seluruh transaksi aplikasi.

Untuk mengatasi limitasi tersebut, arsitektur clustering modern memadukan empat komponen inti:

  • PostgreSQL: Mesin basis data utama yang berjalan dengan replikasi streaming fisik (physical streaming replication).

  • etcd: Distributed Consensus Store berbasis algoritma Raft yang bertindak sebagai single source of truth untuk status kluster, leader election, dan health locking.

  • Patroni: Cluster manager berbasis Python yang memantau PostgreSQL, mengelola replikasi, dan mengeksekusi failover/switchover otomatis secara aman tanpa risiko split-brain.

  • HAProxy: Reverse proxy dan load balancer yang merutekan traffic Read/Write ke node Primary dan traffic Read-Only ke Replicas menggunakan health check berbasis HTTP ke Patroni REST API.

Configuration

Topology

## Install & Configuration ETCD

Step1: Download package & Install package (ALL NODES)

Untuk mengecek versi terbaru bisa dilihat dari link ini https://github.com/etcd-io/etcd

VER=`curl -s https://api.github.com/repos/etcd-io/etcd/releases/latest | grep -Po '"tag_name": "\K.*?(?=")'`
wget https://github.com/etcd-io/etcd/releases/download/$VER/etcd-$VER-linux-amd64.tar.gz
tar xvf etcd-$VER-linux-amd64.tar.gz
cd etcd-$VER-linux-amd64
sudo mv etcd etcdctl etcdutl /usr/local/bin/
sudo useradd --system --home /var/lib/etcd --shell /bin/false etcd
sudo mkdir -p /var/lib/etcd
sudo chown -R etcd:etcd /var/lib/etcd

Step2: Install dependency untuk etcd

sudo apt install -y python3-etcd3 python3-etcd

Step3: Membuat File Service & Allow UFW Firewall

Node1 (psql-01)

  • Membuat service baru dengan nama file/etc/systemd/system/etcd.service

    [Unit]
    Description=etcd
    Documentation=https://github.com/etcd-io/etcd
    After=network.target
    
    [Service]
    User=etcd
    Type=notify
    ExecStart=/usr/local/bin/etcd \
      --name etcd-psql-01 \                                      # Edit here
      --data-dir /var/lib/etcd \
      --listen-client-urls http://0.0.0.0:2379 \
      --advertise-client-urls http://10.100.19.71:2379 \         # Edit here
      --listen-peer-urls http://0.0.0.0:2380 \
      --initial-advertise-peer-urls http://10.100.19.71:2380 \
      --initial-cluster "etcd-psql-01=http://10.100.19.71:2380,etcd-psql-02=http://10.100.19.72:2380,etcd-psql-03=http://10.100.19.73:2380" \                          # Edit here
      --initial-cluster-token etcd-cluster \
      --initial-cluster-state new
    
    Restart=on-failure
    RestartSec=5
    
    [Install]
    WantedBy=multi-user.target
    
  • Menambahkan konfigurasi untuk allow Firewall (UFW)

    sudo ufw allow from 10.100.19.71 to any port 2380 proto tcp
    sudo ufw allow from 10.100.19.72 to any port 2380 proto tcp
    sudo ufw allow from 10.100.19.73 to any port 2380 proto tcp
    sudo ufw allow from 10.100.19.0/24 to any port 2379 proto tcp
    

Node2 (psql-02)

  • Membuat service baru dengan nama file /etc/systemd/system/etcd.service

    [Unit]
    Description=etcd
    Documentation=https://github.com/etcd-io/etcd
    After=network.target
    
    [Service]
    User=etcd
    Type=notify
    ExecStart=/usr/local/bin/etcd \
      --name etcd-psql-02 \                               # Edit here
      --data-dir /var/lib/etcd \
      --listen-client-urls http://0.0.0.0:2379 \
      --advertise-client-urls http://10.100.19.72:2379 \  # Edit here
      --listen-peer-urls http://0.0.0.0:2380 \
      --initial-advertise-peer-urls http://10.100.19.72:2380 \ #Edit Here
      --initial-cluster "etcd-psql-01=http://10.100.19.71:2380,etcd-psql-02=http://10.100.19.72:2380,etcd-psql-03=http://10.100.19.73:2380" \                   #Edit here
      --initial-cluster-token etcd-cluster \
      --initial-cluster-state new
    
    Restart=on-failure
    RestartSec=5
    
    [Install]
    WantedBy=multi-user.target
    
  • Menambahkan konfigurasi untuk allow Firewall (UFW)

    sudo ufw allow from 10.100.19.71 to any port 2380 proto tcp
    sudo ufw allow from 10.100.19.73 to any port 2380 proto tcp
    sudo ufw allow from 10.100.19.73 to any port 2380 proto tcp
    sudo ufw allow from 10.100.19.0/24 to any port 2379 proto tcp
    

Node3 (psql-03)

  • Membuat service baru dengan nama file /etc/systemd/system/etcd.service

    [Unit]
    Description=etcd
    Documentation=https://github.com/etcd-io/etcd
    After=network.target
    
    [Service]
    User=etcd
    Type=notify
    ExecStart=/usr/local/bin/etcd \
      --name etcd-psql-03 \                                          # Edit here
      --data-dir /var/lib/etcd \
      --listen-client-urls http://0.0.0.0:2379 \
      --advertise-client-urls http://10.100.19.73:2379 \             # Edit here
      --listen-peer-urls http://0.0.0.0:2380 \
      --initial-advertise-peer-urls http://10.100.19.73:2380 \
      --initial-cluster "etcd-psql-01=http://10.100.19.71:2380,etcd-psql-02=http://10.100.19.72:2380,etcd-psql-03=http://10.100.19.73:2380" \                              # Edit here
      --initial-cluster-token etcd-cluster \
      --initial-cluster-state new
    
    Restart=on-failure
    RestartSec=5
    
    [Install]
    WantedBy=multi-user.target
    
  • Menambahkan konfigurasi untuk allow Firewall (UFW)

    sudo ufw allow from 10.100.19.71 to any port 2380 proto tcp
    sudo ufw allow from 10.100.19.72 to any port 2380 proto tcp
    sudo ufw allow from 10.100.19.73 to any port 2380 proto tcp
    sudo ufw allow from 10.100.19.0/24 to any port 2379 proto tcp
    

Step4: Restart & Enable service (ALL NODES)

sudo systemctl daemon-reload
sudo systemctl enable etcd
sudo systemctl restart etd

Step5: Checking Service ETCD

  • Check member list

    sudo etcdctl --endpoints=http://10.100.19.71:2379 \
      member list
    
  • Check Health

    sudo etcdctl --endpoints=http://10.100.19.71:2379 \
      endpoint health
    
  • Check Endpoint Status

    sudo etcdctl \
      --endpoints=http://10.100.19.71:2379,http://10.100.19.72:2379,http://10.100.19.73:2379 \
      endpoint status --write-out=table
    

Instalation Postgresql & Patroni

Step1: Update & Install repo untuk postgres (ALL NODES)

sudo apt update && sudo apt upgrade -y
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg

Step2: Install Postgresql 18 & patroni (ALL NODES)

sudo apt update -y && sudo apt upgrade -y
sudo apt install -y postgresql-18 patroni jq 
sudo systemctl disable --now postgresql

Step3: Membuat lib baru untuk data postgresql (ALL NODES)

sudo mkdir -p /var/lib/postgresql/data
sudo chmod 700 /var/lib/postgresql/data/
sudo chown postgres:postgres /var/lib/postgresql/data

Configure Patroni

Step1: Membuat konfigurasi file & Allow Firewall (UFW)

Node1 (psql-01)

  • Membuat config baru dengan nama file /etc/patroni/config.yml

    scope: postgresql-cluster
    namespace: /service/
    name: psql-01                         # Hostname Node1
    
    restapi:
      listen: 0.0.0.0:8008
      connect_address: 10.100.19.71:8008  # IP Node1
    
    etcd3:
      hosts: 10.100.19.71:2379,10.100.19.72:2379,10.100.19.73:2379   # ALL NODES
      protocol: http
    
    bootstrap:
      dcs:
        ttl: 30
        loop_wait: 10
        retry_timeout: 10
        maximum_lag_on_failover: 1048576
        postgresql:
          parameters:
            ssl: 'off'
          pg_hba:
          - host postgres postgres 127.0.0.1/32 scram-sha-256
          - host replication replicator 127.0.0.1/32 scram-sha-256
          - host replication replicator 10.100.19.71/32 scram-sha-256  # Node1
          - host replication replicator 10.100.19.72/32 scram-sha-256  # Node2
          - host replication replicator 10.100.19.73/32 scram-sha-256  # Node3
      initdb:
        - encoding: UTF8
        - data-checksums
    
    postgresql:
      listen: 0.0.0.0:5432
      connect_address: 10.100.19.71:5432              # IP Node1
      data_dir: /var/lib/postgresql/data
      bin_dir: /usr/lib/postgresql/18/bin             # Binary directory for PostgreSQL 18
      authentication:
        superuser:
          username: postgres
          password: aSv5n9FTcBQzIaLPKsxUZBbCBNls58fa  # Superuser password - be sure to change
        replication:
          username: replicator
          password: Q2crBPkMab3TkqvLqbmqwb6OIiKNLwqJ  # Replication password - be sure to change
      parameters:
        max_connections: 100
        shared_buffers: 256MB
        logging_collector: 'on'
        log_directory: '/var/log/postgresql'
        log_filename: 'postgresql-%a.log'
    
    tags:
      nofailover: false
      noloadbalance: false
      clonefrom: false
    
  • Menambahkan konfigurasi untuk allow Firewall (UFW)

    sudo ufw allow from 10.100.19.101 to any port 8008 proto tcp  
    sudo ufw allow from 10.100.19.102 to any port 8008 proto tcp
    sudo ufw allow from 10.100.19.71 to any port 8008 proto tcp
    sudo ufw allow from 10.100.19.72 to any port 8008 proto tcp
    sudo ufw allow from 10.100.19.73 to any port 8008 proto tcp
    sudo ufw allow from 10.100.19.71 to any port 5432 proto tcp
    sudo ufw allow from 10.100.19.72 to any port 5432 proto tcp
    sudo ufw allow from 10.100.19.73 to any port 5432 proto tcp
    
  • Restart service patroni

    sudo systemctl restart patroni
    sudo systemctl enable patroni
    

Node2 (pgsql-02)

  • Membuat config baru dengan nama file /etc/patroni/config.yml

    scope: postgresql-cluster
    namespace: /service/
    name: psql-02                       # Hostname Node2
    
    restapi:
      listen: 0.0.0.0:8008
      connect_address: 10.100.19.72:8008  # IP Node2
    
    etcd3:
      hosts: 10.100.19.71:2379,10.100.19.72:2379,10.100.19.73:2379   # ALL NODES
      protocol: http
    
    bootstrap:
      dcs:
        ttl: 30
        loop_wait: 10
        retry_timeout: 10
        maximum_lag_on_failover: 1048576
        postgresql:
          parameters:
            ssl: 'off'
          pg_hba:
          - host postgres postgres 127.0.0.1/32 scram-sha-256
          - host replication replicator 127.0.0.1/32 scram-sha-256
          - host replication replicator 10.100.19.71/32 scram-sha-256  # Node1
          - host replication replicator 10.100.19.72/32 scram-sha-256  # Node2
          - host replication replicator 10.100.19.73/32 scram-sha-256  # Node3
      initdb:
        - encoding: UTF8
        - data-checksums
    
    postgresql:
      listen: 0.0.0.0:5432
      connect_address: 10.100.19.72:5432              # IP Node2
      data_dir: /var/lib/postgresql/data
      bin_dir: /usr/lib/postgresql/18/bin             # Binary directory for PostgreSQL 18
      authentication:
        superuser:
          username: postgres
          password: aSv5n9FTcBQzIaLPKsxUZBbCBNls58fa  # Superuser password - be sure to change
        replication:
          username: replicator
          password: Q2crBPkMab3TkqvLqbmqwb6OIiKNLwqJ  # Replication password - be sure to change
      parameters:
        max_connections: 100
        shared_buffers: 256MB
        logging_collector: 'on'
        log_directory: '/var/log/postgresql'
        log_filename: 'postgresql-%a.log'
    
    tags:
      nofailover: false
      noloadbalance: false
      clonefrom: false
    
  • Menambahkan konfigurasi untuk allow Firewall (UFW)

    sudo ufw allow from 10.100.19.101 to any port 8008 proto tcp  
    sudo ufw allow from 10.100.19.102 to any port 8008 proto tcp
    sudo ufw allow from 10.100.19.71 to any port 8008 proto tcp
    sudo ufw allow from 10.100.19.72 to any port 8008 proto tcp
    sudo ufw allow from 10.100.19.73 to any port 8008 proto tcp
    sudo ufw allow from 10.100.19.71 to any port 5432 proto tcp
    sudo ufw allow from 10.100.19.72 to any port 5432 proto tcp
    sudo ufw allow from 10.100.19.73 to any port 5432 proto tcp
    
  • Restart service patroni

    sudo systemctl restart patroni
    sudo systemctl enable patroni
    

Node3 (psql-03)

  • Membuat config baru dengan nama file /etc/patroni/config.yml

    scope: postgresql-cluster
    namespace: /service/
    name: psql-03                       # Hostname Node3
    
    restapi:
      listen: 0.0.0.0:8008
      connect_address: 10.100.19.73:8008  # IP Node3
    
    etcd3:
      hosts: 10.100.19.71:2379,10.100.19.72:2379,10.100.19.73:2379   # ALL NODES
      protocol: http
    
    bootstrap:
      dcs:
        ttl: 30
        loop_wait: 10
        retry_timeout: 10
        maximum_lag_on_failover: 1048576
        postgresql:
          parameters:
            ssl: 'off'
          pg_hba:
          - host postgres postgres 127.0.0.1/32 scram-sha-256
          - host replication replicator 127.0.0.1/32 scram-sha-256
          - host replication replicator 10.100.19.71/32 scram-sha-256  # Node1
          - host replication replicator 10.100.19.72/32 scram-sha-256  # Node2
          - host replication replicator 10.100.19.73/32 scram-sha-256  # Node3
      initdb:
        - encoding: UTF8
        - data-checksums
    
    postgresql:
      listen: 0.0.0.0:5432
      connect_address: 10.100.19.73:5432              # IP Node3
      data_dir: /var/lib/postgresql/data
      bin_dir: /usr/lib/postgresql/18/bin             # Binary directory for PostgreSQL 18
      authentication:
        superuser:
          username: postgres
          password: aSv5n9FTcBQzIaLPKsxUZBbCBNls58fa  # Superuser password - be sure to change
        replication:
          username: replicator
          password: Q2crBPkMab3TkqvLqbmqwb6OIiKNLwqJ  # Replication password - be sure to change
      parameters:
        max_connections: 100
        shared_buffers: 256MB
        logging_collector: 'on'
        log_directory: '/var/log/postgresql'
        log_filename: 'postgresql-%a.log'
    
    tags:
      nofailover: false
      noloadbalance: false
      clonefrom: false
    
  • Menambahkan konfigurasi untuk allow Firewall (UFW)

    sudo ufw allow from 10.100.19.101 to any port 8008 proto tcp  
    sudo ufw allow from 10.100.19.102 to any port 8008 proto tcp
    sudo ufw allow from 10.100.19.71 to any port 8008 proto tcp
    sudo ufw allow from 10.100.19.72 to any port 8008 proto tcp
    sudo ufw allow from 10.100.19.73 to any port 8008 proto tcp
    sudo ufw allow from 10.100.19.71 to any port 5432 proto tcp
    sudo ufw allow from 10.100.19.72 to any port 5432 proto tcp
    sudo ufw allow from 10.100.19.73 to any port 5432 proto tcp
    sudo ufw allow from 10.100.19.101 to any port 5432 proto tcp
    sudo ufw allow from 10.100.19.102 to any port 5432 proto tcp
    
  • Restart service patroni

    sudo systemctl restart patroni
    sudo systemctl enable patroni
    

Step2: Check Health

curl -s -k http://10.100.19.71:8008/primary | jq .
curl -s -k http://10.100.19.72:8008/primary | jq .
curl -s -k http://10.100.19.73:8008/primary | jq .

### Step3: Check config menggunakan patronictl

sudo patronictl -c /etc/patroni/config.yml show-config
sudo patronictl -c /etc/patroni/config.yml list

## Konfigurasi Haproxy

Step1: Tambahkan pada baris konfigurasi Haproxy /etc/haproxy/haproxy.cfd

frontend postgres_frontend
    bind *:5432
    mode tcp
    default_backend postgres_backend

backend postgres_backend
    mode tcp
    option tcp-check
    option httpchk GET /primary  # patroni provides an endpoint to check node roles
    http-check expect status 200  # expect 200 for the primary node
    timeout connect 5s
    timeout server 30s
    server psql-01 10.100.19.71:5432 check port 8008
    server psql-02 10.100.19.72:5432 check port 8008
    server psql-03 10.100.19.73:5432 check port 8008

Untuk konfigurasi ini akan melakukan health check pada status primary aktif. Jika primary tidak aktif, maka nodes lainnya akan down

Step2: Restart service haproxy & Check status pada haprxy

sudo systemctl restart haproxy

Kita bisa chcek menggunakan stats monitor

## Konfigurasi pg_hba di patroni

Tadi kita sudah membuat konfigurasi patroni, tetapi kita masih blm membuat allow list untuk all user agar bisa akses dari luar. Saatnya kita akan membuat konfigurasi pg_hba via patroni.

Step1: Jalankan command patroni edit

sudo patronictl -c /etc/patroni/config.yml edit-config

Step2: Tambahkan code dibawah

  - host all postgres 0.0.0.0/0 reject
  - host all all 0.0.0.0/0 scram-sha-256

### Step3: Reload patroni config

sudo patronictl -c /etc/patroni/config.yml reload postgresql-cluster

## Membuat Superuser

Superuser bawaan dari postgres itu hanya bisa diakses dari lokal server, yang artinya kita harus membuat superuser agar kita bisa remote

Step1: Login dari primary nodes

psql -U postgres -h 127.0.0.1

Step2: Create user dan kasih akses privileges

CREATE ROLE adminpostgres WITH SUPERUSER LOGIN PASSWORD 'password_rahasia';
\du

### Step3: Login menggunakan IP address dari haproxy menggunakan user yang sudah dibuat

Selesai