Yes — here’s a **clean, ordered, end-to-end install guide** you can follow exactly.

I’ll structure it like a runbook:

* Phase 0: prerequisites
* Phase 1: all servers (common setup)
* Phase 2: WireGuard mesh
* Phase 3: DB servers (db1, db2, db3)
* Phase 4: proxy server (HAProxy + Caddy + pgAdmin + PostgREST)
* Phase 5: initialize database
* Phase 6: validation
* Phase 7: failover test

No fluff, just steps.

---

# 🧭 Phase 0 — Prerequisites

You need:

* 4 VPS machines:

  * `db1`
  * `db2`
  * `db3`
  * `proxy1`

* Public IPs for each

* Root or sudo access

---

# 🧭 Phase 1 — Common setup (ALL servers)

Run on **all 4 servers**

```bash
apt update && apt upgrade -y

apt install -y docker.io docker-compose wireguard curl ufw

systemctl enable docker
systemctl start docker
```

---

# 🧭 Phase 2 — WireGuard setup (ALL servers)

## 2.1 Generate keys (each server)

```bash
umask 077
wg genkey | tee /etc/wireguard/privatekey | wg pubkey > /etc/wireguard/publickey
```

Save all public keys.

---

## 2.2 Assign IPs

```
db1    → 10.20.0.11
db2    → 10.20.0.12
db3    → 10.20.0.13
proxy1 → 10.20.0.21
```

---

## 2.3 Create `/etc/wireguard/wg0.conf`

(Example: db1 — repeat pattern for others)

```ini
[Interface]
Address = 10.20.0.11/24
ListenPort = 51820
PrivateKey = <DB1_PRIVATE_KEY>

[Peer]
PublicKey = <DB2_PUBLIC_KEY>
AllowedIPs = 10.20.0.12/32
Endpoint = DB2_PUBLIC_IP:51820
PersistentKeepalive = 25

[Peer]
PublicKey = <DB3_PUBLIC_KEY>
AllowedIPs = 10.20.0.13/32
Endpoint = DB3_PUBLIC_IP:51820
PersistentKeepalive = 25

[Peer]
PublicKey = <PROXY_PUBLIC_KEY>
AllowedIPs = 10.20.0.21/32
Endpoint = PROXY_PUBLIC_IP:51820
PersistentKeepalive = 25
```

---

## 2.4 Start WireGuard

```bash
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0
```

---

## 2.5 Verify

```bash
ping 10.20.0.12
ping 10.20.0.13
ping 10.20.0.21
```

---

# 🧭 Phase 3 — DB servers (db1, db2, db3)

## 3.1 Create working dir

```bash
mkdir -p ~/db-stack
cd ~/db-stack
```

---

## 3.2 Create files

### `Dockerfile`

```dockerfile
FROM postgres:16-bookworm

RUN apt-get update && \
    apt-get install -y python3 python3-pip postgresql-16-cron && \
    pip3 install patroni[etcd3] && \
    rm -rf /var/lib/apt/lists/*
```

---

### `patroni.yml`

(IMPORTANT: change NODE_NAME + IP per server)

```yaml
scope: pg-ha-demo
namespace: /service/
name: db1

restapi:
  listen: 10.20.0.11:8008
  connect_address: 10.20.0.11:8008

etcd3:
  hosts:
    - 10.20.0.11:2379
    - 10.20.0.12:2379
    - 10.20.0.13:2379

bootstrap:
  dcs:
    postgresql:
      parameters:
        shared_preload_libraries: "pg_cron"
        cron.database_name: "appdb"

postgresql:
  listen: 10.20.0.11:5432
  connect_address: 10.20.0.11:5432
  data_dir: /var/lib/postgresql/data

  authentication:
    superuser:
      username: postgres
      password: postgrespass
    replication:
      username: replicator
      password: replpass
```

---

### `docker-compose.yml`

```yaml
services:
  etcd:
    image: quay.io/coreos/etcd:v3.5.15
    network_mode: host
    environment:
      ETCD_NAME: db1
      ETCD_INITIAL_CLUSTER: db1=http://10.20.0.11:2380,db2=http://10.20.0.12:2380,db3=http://10.20.0.13:2380
      ETCD_INITIAL_CLUSTER_STATE: new
      ETCD_LISTEN_CLIENT_URLS: http://10.20.0.11:2379
      ETCD_LISTEN_PEER_URLS: http://10.20.0.11:2380

  patroni:
    build: .
    network_mode: host
    volumes:
      - ./patroni.yml:/etc/patroni.yml
      - ./data:/var/lib/postgresql/data
    command: patroni /etc/patroni.yml
```

---

## 3.3 Start DB cluster

Run on ALL THREE nodes:

```bash
docker compose up -d --build
```

---

## 3.4 Verify cluster

On any node:

```bash
docker exec -it <container> patronictl -c /etc/patroni.yml list
```

You should see:

* 1 Leader
* 2 Replicas

---

# 🧭 Phase 4 — Proxy server (proxy1)

## 4.1 Setup

```bash
mkdir -p ~/proxy-stack
cd ~/proxy-stack
```

---

## 4.2 Create `haproxy.cfg`

```cfg
frontend postgres
    bind *:5432
    default_backend patroni_primary

backend patroni_primary
    option httpchk GET /primary
    server db1 10.20.0.11:5432 check port 8008
    server db2 10.20.0.12:5432 check port 8008
    server db3 10.20.0.13:5432 check port 8008
```

---

## 4.3 Create `Caddyfile`

```caddy
pgadmin.example.com {
    basic_auth {
        admin <HASH>
    }
    reverse_proxy 127.0.0.1:8080
}

api.example.com {
    basic_auth {
        apiuser <HASH>
    }
    reverse_proxy 127.0.0.1:3000
}
```

---

## 4.4 Generate password

```bash
docker run --rm caddy:2 caddy hash-password --plaintext 'password'
```

---

## 4.5 Create `docker-compose.yml`

```yaml
services:
  haproxy:
    image: haproxy:3.0
    network_mode: host
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg

  pgadmin:
    image: dpage/pgadmin4
    ports:
      - "127.0.0.1:8080:80"

  postgrest:
    image: postgrest/postgrest
    environment:
      PGRST_DB_URI: postgres://postgres:postgrespass@127.0.0.1:5432/appdb
    ports:
      - "127.0.0.1:3000:3000"

  caddy:
    image: caddy:2
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
```

---

## 4.6 Start proxy stack

```bash
docker compose up -d
```

---

# 🧭 Phase 5 — Initialize database

Connect:

```bash
psql -h <proxy-ip> -U postgres
```

Run:

```sql
CREATE DATABASE appdb;
\c appdb
CREATE EXTENSION pg_cron;
```

---

# 🧭 Phase 6 — Validate

### DB access

```bash
psql -h proxy_ip -p 5432 -U postgres
```

---

### pgAdmin

```
https://pgadmin.example.com
```

---

### PostgREST

```
https://api.example.com
```

---

# 🧭 Phase 7 — Failover test

Stop primary:

```bash
docker stop patroni
```

Wait ~10 seconds.

Then:

```bash
psql -h proxy_ip
```

👉 Should still connect

---

# 🧠 Final notes

* Apps must retry connections
* Always add node before removing
* Backups still required
* WireGuard is your security layer

