15% off your workspace - subscribe to our blogNo per-service fees - one plan, unlimited appsDeploy in under 40 seconds99.95% uptime SLAFree tier available - start building today15% off your workspace - subscribe to our blogNo per-service fees - one plan, unlimited appsDeploy in under 40 seconds99.95% uptime SLAFree tier available - start building today
Miget x AIPlansCompareBlogDashboard
Start for Free
Blog/Miget/PostgreSQL/
·

Set Up a PostgreSQL Disaster Recovery Site Outside Miget

Your production database runs on Miget. But what if you need a replica running outside Miget - on your own infrastructure, another cloud, or a VPS - for disaster recovery, compliance, or peace of mind?

Miget's External Replication feature lets you set up a streaming PostgreSQL replica anywhere. You get TLS client certificates for secure connectivity, and read-only S3 credentials for WAL archive access. This guide walks through the full setup.


What You Get

When you enable external replication on a Miget PostgreSQL database, the platform provides:

  • Public endpoint - your database exposed at {cluster}.db.{region}.onmiget.com:5432
  • TLS certificates - CA cert, client cert, and client key for mutual TLS authentication
  • Streaming replication user - streaming_replica with replication privileges
  • S3 WAL archive access - read-only credentials scoped to your database's backup prefix

The replica uses PostgreSQL's native streaming replication protocol. No special plugins or agents needed - just a standard PostgreSQL 17 installation.


Prerequisites

  • A Miget PostgreSQL database (CloudNative PG) with external replication enabled
  • A server with Ubuntu 22.04 (or any Linux distro that supports PostgreSQL 17)
  • Network access to your Miget database endpoint on port 5432

Step 1: Enable External Replication

In your Miget dashboard, navigate to your PostgreSQL service and enable External Replication. The dashboard displays several sections with copyable fields:

Connection Details

  • Host - e.g., postgres-service-ro0bg.db.eu-east-1.onmiget.com
  • Port - 5432
  • Server Name - e.g., postgres-service-ro0bg
  • Streaming User - streaming_replica

TLS Certificates

  • CA Certificate - base64-encoded CA cert
  • TLS Certificate - base64-encoded client cert
  • TLS Key - base64-encoded client key

S3 Storage

  • Endpoint - e.g., https://m2.eu-east-1.onmiget.com
  • Bucket - s3-cnpg-backups
  • Path - e.g., migetbgq/postgres-service-ro0bg/

Copy these values - you'll need them throughout this guide.


Step 2: Install PostgreSQL 17

On your DR server, install PostgreSQL 17 from the official PGDG repository:

sudo apt-get update
sudo apt-get install -y curl ca-certificates gnupg lsb-release

# Add PostgreSQL APT repository
sudo install -d /usr/share/postgresql-common/pgdg
curl -so /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \
  https://www.postgresql.org/media/keys/ACCC4CF8.asc

echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] \
  https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \
  | sudo tee /etc/apt/sources.list.d/pgdg.list

sudo apt-get update
sudo apt-get install -y postgresql-17

Verify the installation:

pg_lsclusters
# Ver Cluster Port Status Owner    Data directory              Log file
# 17  main    5432 online postgres /var/lib/postgresql/17/main  ...

Step 3: Set Up TLS Certificates

Create a directory for the certificates and decode them from the payload:

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

# Decode and write each certificate (replace with your base64 values)
echo '<base64 ca_crt>' | sudo -u postgres base64 -d \
  > /var/lib/postgresql/17/certs/ca.crt

echo '<base64 tls_crt>' | sudo -u postgres base64 -d \
  > /var/lib/postgresql/17/certs/tls.crt

echo '<base64 tls_key>' | sudo -u postgres base64 -d \
  > /var/lib/postgresql/17/certs/tls.key

# Restrict key permissions
sudo -u postgres chmod 600 /var/lib/postgresql/17/certs/tls.key

Step 4: Take a Base Backup

Stop the local PostgreSQL instance, wipe the data directory, and take a base backup from the Miget primary:

sudo systemctl stop postgresql@17-main

# Wipe the default data directory
sudo -u postgres rm -rf /var/lib/postgresql/17/main/*

# Take base backup with streaming replication setup (-R creates standby.signal)
sudo -u postgres pg_basebackup \
  -d "host=my-database.db.eu-east-1.onmiget.com \
      port=5432 \
      user=streaming_replica \
      sslmode=verify-ca \
      sslcert=/var/lib/postgresql/17/certs/tls.crt \
      sslkey=/var/lib/postgresql/17/certs/tls.key \
      sslrootcert=/var/lib/postgresql/17/certs/ca.crt" \
  -D /var/lib/postgresql/17/main \
  -Fp -Xs -P -R

The -R flag automatically creates standby.signal and writes the primary_conninfo to postgresql.auto.conf. Your replica is pre-configured to stream from the primary.


Step 5: Match Primary Configuration

Miget's PostgreSQL clusters run with higher resource limits than the default. Your replica must match these settings or it will refuse to start:

sudo -u postgres tee /etc/postgresql/17/main/conf.d/replica.conf << 'EOF'
# Match primary settings
max_worker_processes = 32
max_connections = 200
max_prepared_transactions = 0
max_locks_per_transaction = 64
max_wal_senders = 10
EOF

Step 6: Configure S3 WAL Archive Restore

Streaming replication covers the live WAL stream, but if the replica falls behind (network outage, server restart), it needs to fetch missed WAL segments from the S3 archive. Install barman-cli-cloud and configure restore_command:

# Install barman-cloud
sudo apt-get install -y barman-cli-cloud

# Add restore_command to the replica config
sudo -u postgres tee -a /etc/postgresql/17/main/conf.d/replica.conf << 'EOF'

# WAL archive restore from S3 (fallback when streaming is interrupted)
restore_command = 'AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXX \
  AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
  barman-cloud-wal-restore \
  --cloud-provider aws-s3 \
  --endpoint-url https://m2.eu-east-1.onmiget.com \
  s3://s3-cnpg-backups/namespace/my-database \
  my-database %f %p'
EOF

Replace the access key, secret key, endpoint, bucket path, and server name with the values from your dashboard.

The S3 credentials are read-only and scoped to your database's backup prefix. They cannot access other databases' backups or write any data.


Step 7: Start the Replica

sudo systemctl start postgresql@17-main

Verify the replica is running and streaming:

sudo -u postgres psql -c \
  "SELECT pg_is_in_recovery(), pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();"

#  pg_is_in_recovery | pg_last_wal_receive_lsn | pg_last_wal_replay_lsn
# -------------------+-------------------------+------------------------
#  t                 | 0/7B000060              | 0/7B000060

Check the streaming connection:

sudo -u postgres psql -c "SELECT status, sender_host FROM pg_stat_wal_receiver;"

#   status   |                  sender_host
# -----------+------------------------------------------------
#  streaming | my-database.db.eu-east-1.onmiget.com

Step 8: Test Replication

Write data on the primary through your application or psql, then read it on the replica:

# On the replica - verify data appears
sudo -u postgres psql -d mydb -c "SELECT count(*) FROM my_table;"

Changes on the primary should appear on the replica within milliseconds.


How It Works

The replica maintains two WAL recovery paths:

  1. Streaming replication (primary path) - direct TCP connection to the Miget primary over TLS. Receives WAL records in real-time with sub-second lag.

  2. S3 WAL archive (fallback path) - if streaming is interrupted, PostgreSQL automatically fetches archived WAL segments from Miget's S3-compatible object storage using barman-cloud-wal-restore.

This dual-path approach means your replica can recover from network interruptions without data loss, as long as the WAL archive retention hasn't expired.

┌─────────────┐     Streaming (TLS)      ┌──────────────┐
│ Miget Primary│ ──────────────────────── │  DR Replica   │
│  PostgreSQL  │                          │  (Your VPS)   │
└──────┬───────┘                          └───────┬───────┘
       │                                          │
       │  WAL Archive                             │ restore_command
       ▼                                          ▼
┌─────────────────────────────────────────────────────────┐
│              Miget S3 Object Storage                     │
│        s3://bucket/namespace/my-database/                │
└─────────────────────────────────────────────────────────┘

Security

All connections are secured with mutual TLS:

  • The primary's CA certificate (ca.crt) verifies the server identity
  • The client certificate and key (tls.crt, tls.key) authenticate the replica
  • No passwords are transmitted - authentication is certificate-based
  • S3 credentials are read-only and restricted to your database's backup prefix

Disabling External Replication

When you no longer need the external replica, disable external replication in your Miget dashboard. This will:

  • Remove the replication endpoint (if not used for public access)
  • Revoke the read-only S3 credentials
  • Clean up the bucket policy

Your replica will stop receiving updates but the local data remains intact.


Next Steps

  • Promotion - to promote the replica to a standalone primary, remove standby.signal and restart PostgreSQL
  • Monitoring - set up alerts on pg_stat_wal_receiver to detect replication lag
  • Automated failover - combine with your existing HA tooling (Patroni, repmgr) for automated promotion
Set Up a PostgreSQL Disaster Recovery Site Outside Miget - Miget Blog | Miget