Set up TimescaleDB high availability with streaming replication and automatic failover

Advanced 45 min Apr 22, 2026 656 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Configure TimescaleDB with PostgreSQL streaming replication for high availability. Set up primary and standby servers with hot standby mode, implement automatic failover with pg_auto_failover, and monitor replication status for production-ready time-series database clustering.

Prerequisites

  • At least 2 servers with 4GB RAM
  • PostgreSQL 16 compatible system
  • Network connectivity between servers
  • Basic knowledge of PostgreSQL administration

What this solves

TimescaleDB high availability ensures your time-series database remains operational during server failures, hardware issues, or maintenance windows. This tutorial sets up PostgreSQL streaming replication with TimescaleDB extensions, configures automatic failover using pg_auto_failover, and establishes monitoring for replication lag and cluster health.

Step-by-step installation

Update system packages

Start by updating your package manager on both primary and standby servers to ensure you get the latest versions.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install PostgreSQL and TimescaleDB

Install PostgreSQL 16 and TimescaleDB on both primary and standby servers. TimescaleDB provides time-series optimizations on top of PostgreSQL.

wget --quiet -O - https://packagecloud.io/timescale/timescaledb/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/timescaledb.gpg
echo "deb [signed-by=/usr/share/keyrings/timescaledb.gpg] https://packagecloud.io/timescale/timescaledb/ubuntu/ $(lsb_release -c -s) main" | sudo tee /etc/apt/sources.list.d/timescaledb.list
sudo apt update
sudo apt install -y postgresql-16 postgresql-client-16 timescaledb-2-postgresql-16
sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
echo "[timescale_timescaledb]
name=timescale_timescaledb
baseurl=https://packagecloud.io/timescale/timescaledb/el/\$releasever/\$basearch
repo_gpgcheck=1
gpgcheck=0
enabled=1
gpgkey=https://packagecloud.io/timescale/timescaledb/gpgkey
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
metadata_expire=300" | sudo tee /etc/yum.repos.d/timescale_timescaledb.repo
sudo dnf install -y postgresql16-server postgresql16 timescaledb-2-postgresql-16

Initialize PostgreSQL database (AlmaLinux/Rocky only)

On AlmaLinux and Rocky Linux systems, manually initialize the PostgreSQL database cluster.

# Database is automatically initialized on Ubuntu/Debian
echo "PostgreSQL automatically initialized"
sudo /usr/pgsql-16/bin/postgresql-16-setup initdb
sudo systemctl enable postgresql-16
sudo systemctl start postgresql-16

Configure TimescaleDB extension

Add TimescaleDB to PostgreSQL's shared libraries and tune the configuration for time-series workloads.

sudo timescaledb-tune --quiet --yes

Configure primary server for replication

On the primary server, configure PostgreSQL for streaming replication by enabling WAL archiving and setting replication parameters.

# Replication settings
listen_addresses = '*'
wal_level = replica
max_wal_senders = 3
max_replication_slots = 3
wal_keep_size = 1GB
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/16/main/archive/%f'

# TimescaleDB settings
shared_preload_libraries = 'timescaledb'
max_connections = 200
shared_buffers = 256MB
effective_cache_size = 1GB
work_mem = 4MB
maintenance_work_mem = 64MB

Create archive directory

Create the WAL archive directory with proper permissions for the postgres user.

sudo mkdir -p /var/lib/postgresql/16/main/archive
sudo chown postgres:postgres /var/lib/postgresql/16/main/archive
sudo chmod 750 /var/lib/postgresql/16/main/archive

Configure authentication for replication

Set up host-based authentication to allow the standby server to connect for replication.

# Add these lines (replace 203.0.113.20 with your standby server IP)
host    replication     replicator      203.0.113.20/32         scram-sha-256
host    all             postgres        203.0.113.20/32         scram-sha-256

Create replication user

Create a dedicated user for replication with appropriate privileges.

sudo systemctl restart postgresql
sudo -u postgres psql -c "CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'SecureReplicationPass123';"
sudo -u postgres psql -c "CREATE EXTENSION IF NOT EXISTS timescaledb;"

Test primary server setup

Verify that TimescaleDB is properly installed and the primary server is ready for replication.

sudo -u postgres psql -c "SELECT version();"
sudo -u postgres psql -c "\dx timescaledb"
sudo -u postgres psql -c "SELECT * FROM pg_stat_replication;"

Create base backup for standby

On the standby server, create a base backup from the primary server to initialize the replica.

sudo systemctl stop postgresql
sudo -u postgres rm -rf /var/lib/postgresql/16/main/*
sudo -u postgres pg_basebackup -h 203.0.113.10 -D /var/lib/postgresql/16/main -U replicator -W -v -P -R

Configure standby server

Configure the standby server for hot standby mode and streaming replication.

# Hot standby settings
hot_standby = on
hot_standby_feedback = on
wal_receiver_timeout = 60s
max_standby_streaming_delay = 30s
max_standby_archive_delay = 30s

# TimescaleDB settings (same as primary)
shared_preload_libraries = 'timescaledb'
max_connections = 200
shared_buffers = 256MB
effective_cache_size = 1GB

Start standby server

Start the standby server and verify it connects to the primary for streaming replication.

sudo systemctl start postgresql
sudo systemctl enable postgresql
sudo -u postgres psql -c "SELECT pg_is_in_recovery();"
sudo -u postgres psql -c "SELECT * FROM pg_stat_wal_receiver;"

Install pg_auto_failover

Install pg_auto_failover for automatic failover management on both servers.

sudo apt install -y postgresql-16-auto-failover
sudo dnf install -y pg_auto_failover_16

Set up monitor node

Initialize the pg_auto_failover monitor on a separate server or the primary server.

export PGDATA=/var/lib/postgresql/monitor
sudo -u postgres mkdir -p /var/lib/postgresql/monitor
sudo -u postgres pg_autoctl create monitor --hostname 203.0.113.10 --pgdata /var/lib/postgresql/monitor

Register primary server with monitor

Register the primary TimescaleDB server with the pg_auto_failover monitor.

export PGDATA=/var/lib/postgresql/16/main
sudo -u postgres pg_autoctl create postgres --hostname 203.0.113.10 --pgdata /var/lib/postgresql/16/main --monitor 'postgres://autoctl_node@203.0.113.10:5432/pg_auto_failover'

Register standby server with monitor

Register the standby TimescaleDB server with the monitor for automatic failover.

export PGDATA=/var/lib/postgresql/16/main
sudo -u postgres pg_autoctl create postgres --hostname 203.0.113.20 --pgdata /var/lib/postgresql/16/main --monitor 'postgres://autoctl_node@203.0.113.10:5432/pg_auto_failover'

Create systemd services for pg_auto_failover

Create systemd services to manage pg_auto_failover automatically.

[Unit]
Description=pg_auto_failover
Requires=postgresql.service
After=postgresql.service
Requisite=postgresql.service

[Service]
Type=exec
User=postgres
Group=postgres
ExecStart=/usr/lib/postgresql/16/bin/pg_autoctl run
Environment=PGDATA=/var/lib/postgresql/16/main
Restart=always
RestartSec=5
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable pgautofailover
sudo systemctl start pgautofailover

Configure connection pooling

Set up connection pooling to handle failover transparently for applications.

sudo apt install -y pgbouncer
sudo dnf install -y pgbouncer
[databases]
timescaledb = host=203.0.113.10 port=5432 dbname=postgres

[pgbouncer]
listen_port = 6432
listen_addr = *
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 200
default_pool_size = 20

Step-by-step configuration

Create monitoring database

Create a dedicated database for monitoring TimescaleDB replication metrics.

sudo -u postgres createdb tsdb_monitor
sudo -u postgres psql -d tsdb_monitor -c "CREATE EXTENSION IF NOT EXISTS timescaledb;"
sudo -u postgres psql -d tsdb_monitor -c "CREATE TABLE replication_lag (time TIMESTAMPTZ NOT NULL, lag_seconds REAL, lag_bytes BIGINT);"
sudo -u postgres psql -d tsdb_monitor -c "SELECT create_hypertable('replication_lag', 'time');"

Set up replication monitoring

Create a script to monitor replication lag and store metrics in TimescaleDB.

#!/bin/bash
set -e

# Get replication lag from primary server
LAG_SECONDS=$(sudo -u postgres psql -t -c "SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::int;" 2>/dev/null || echo "0")
LAG_BYTES=$(sudo -u postgres psql -t -c "SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) FROM pg_stat_replication WHERE application_name = 'walreceiver';" 2>/dev/null || echo "0")

# Insert metrics into monitoring table
sudo -u postgres psql -d tsdb_monitor -c "INSERT INTO replication_lag (time, lag_seconds, lag_bytes) VALUES (NOW(), $LAG_SECONDS, $LAG_BYTES);"

echo "Replication lag: ${LAG_SECONDS}s, ${LAG_BYTES} bytes"
sudo chmod +x /usr/local/bin/monitor-replication.sh

Configure automated monitoring

Set up a cron job to collect replication metrics every minute.

echo "* * * * * /usr/local/bin/monitor-replication.sh >> /var/log/replication-monitor.log 2>&1" | sudo -u postgres crontab -

Create sample time-series data

Create a sample hypertable to test TimescaleDB replication functionality.

sudo -u postgres psql -d postgres -c "CREATE TABLE sensor_data (time TIMESTAMPTZ NOT NULL, sensor_id INT, temperature REAL, humidity REAL);"
sudo -u postgres psql -d postgres -c "SELECT create_hypertable('sensor_data', 'time');"
sudo -u postgres psql -d postgres -c "INSERT INTO sensor_data VALUES (NOW(), 1, 22.5, 45.2), (NOW(), 2, 23.1, 44.8);"

Verify your setup

Test the TimescaleDB high availability setup with these verification commands.

# Check PostgreSQL and TimescaleDB status
sudo -u postgres psql -c "SELECT version();"
sudo -u postgres psql -c "\dx timescaledb"

# Check replication status
sudo -u postgres psql -c "SELECT * FROM pg_stat_replication;"
sudo -u postgres psql -c "SELECT * FROM pg_replication_slots;"

# Verify pg_auto_failover status
sudo -u postgres pg_autoctl show state
sudo -u postgres pg_autoctl show events
# Verify standby server is in recovery mode
sudo -u postgres psql -c "SELECT pg_is_in_recovery();"

# Check WAL receiver status
sudo -u postgres psql -c "SELECT * FROM pg_stat_wal_receiver;"

# Test read-only access to replicated data
sudo -u postgres psql -c "SELECT COUNT(*) FROM sensor_data;"
# Simulate primary server failure
sudo systemctl stop postgresql

# Verify automatic failover (run on former standby)
sudo -u postgres pg_autoctl show state

# Test write access on new primary
sudo -u postgres psql -c "INSERT INTO sensor_data VALUES (NOW(), 3, 21.8, 46.5);"

Monitor replication status and performance

Set up alerting for replication lag

Create a script to alert when replication lag exceeds thresholds.

#!/bin/bash
set -e

MAX_LAG_SECONDS=30
MAX_LAG_BYTES=16777216  # 16MB

# Get current replication lag
LAG_SECONDS=$(sudo -u postgres psql -t -c "SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::int;" 2>/dev/null || echo "999")
LAG_BYTES=$(sudo -u postgres psql -t -c "SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) FROM pg_stat_replication WHERE application_name = 'walreceiver';" 2>/dev/null || echo "999999999")

# Check thresholds and alert
if [[ $LAG_SECONDS -gt $MAX_LAG_SECONDS ]] || [[ $LAG_BYTES -gt $MAX_LAG_BYTES ]]; then
    echo "ALERT: Replication lag exceeded thresholds - ${LAG_SECONDS}s, ${LAG_BYTES} bytes" | logger -t timescaledb-replication
    # Add email/Slack notification here
fi

echo "Replication health check: OK (lag: ${LAG_SECONDS}s, ${LAG_BYTES} bytes)"
sudo chmod +x /usr/local/bin/check-replication-health.sh
echo "*/5 * * * * /usr/local/bin/check-replication-health.sh" | sudo crontab -

Monitor cluster with pg_auto_failover

Use pg_auto_failover commands to monitor cluster health and perform maintenance.

# Show current cluster state
sudo -u postgres pg_autoctl show state

# Show cluster events and history
sudo -u postgres pg_autoctl show events --count 10

# Show cluster configuration
sudo -u postgres pg_autoctl config show

# Enable maintenance mode for updates
</code><h2><code>sudo -u postgres pg_autoctl enable maintenance</code></h2></pre>
</div>

# Test failover scenarios and recovery procedures

<div class="step">
### Test automatic failover
<p>Simulate primary server failure and verify automatic promotion of standby.</p>
<pre class="terminal"><code># Stop primary PostgreSQL to trigger failover
sudo systemctl stop postgresql
sudo systemctl stop pgautofailover
# Watch failover process
sudo -u postgres pg_autoctl watch

# Verify new primary status (run on former standby)
sudo -u postgres psql -c "SELECT pg_is_in_recovery();"
sudo -u postgres psql -c "INSERT INTO sensor_data VALUES (NOW(), 4, 20.5, 47.2);"

Test manual failover

Perform planned failover for maintenance scenarios.

# Enable maintenance mode on primary
sudo -u postgres pg_autoctl enable maintenance

# Perform manual failover
sudo -u postgres pg_autoctl perform failover

# Verify cluster state after failover
sudo -u postgres pg_autoctl show state

Test recovery procedures

Restore the original primary server and add it back to the cluster.

# Restart services after fixing issues
sudo systemctl start postgresql
sudo systemctl start pgautofailover

# Verify node rejoins as standby
sudo -u postgres pg_autoctl show state
sudo -u postgres psql -c "SELECT pg_is_in_recovery();"

# Test data consistency
sudo -u postgres psql -c "SELECT COUNT(*) FROM sensor_data;"

Create backup and recovery strategy

Set up automated backups that work with your high availability setup.

#!/bin/bash
set -e

BACKUP_DIR="/var/backups/timescaledb"
DATE=$(date +%Y%m%d_%H%M%S)

# Create backup directory
sudo mkdir -p $BACKUP_DIR

# Create logical backup
sudo -u postgres pg_dumpall --clean --if-exists > $BACKUP_DIR/full_backup_$DATE.sql

# Create WAL archive backup
sudo tar -czf $BACKUP_DIR/wal_archive_$DATE.tar.gz /var/lib/postgresql/16/main/archive/

# Cleanup old backups (keep 7 days)
find $BACKUP_DIR -name "*.sql" -mtime +7 -delete
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete

echo "Backup completed: $BACKUP_DIR/full_backup_$DATE.sql"
sudo chmod +x /usr/local/bin/timescaledb-backup.sh
echo "0 2 * * * /usr/local/bin/timescaledb-backup.sh >> /var/log/timescaledb-backup.log 2>&1" | sudo crontab -

Common issues

Automated install script

Run this to automate the entire setup

不想自己管理这些吗?

我们为依赖稳定运行时间的企业管理基础设施。全托管服务,配备一位熟悉您系统架构的固定联系人。

您将拥有一位了解您整体架构的固定联系人

Rotterdam 12:57 · 一条消息即可联系我们,无需填写工单表单