Set up pgBackRest with TimescaleDB for automated backups, point-in-time recovery, and database protection. Includes configuration for local and remote repositories, scheduled backups, and comprehensive restore procedures.
Prerequisites
- TimescaleDB or PostgreSQL 12+ installed
- Root or sudo access
- At least 10GB free disk space
What this solves
pgBackRest provides reliable backup and point-in-time recovery for TimescaleDB and PostgreSQL databases. This tutorial configures automated backups with retention policies, compression, and secure remote storage options to protect your time-series data from hardware failures and human errors.
Step-by-step configuration
Update system packages
Start by updating your package manager to ensure you get the latest versions.
sudo apt update && sudo apt upgrade -y
Install pgBackRest
Install pgBackRest from the official package repositories.
sudo apt install -y pgbackrest
Create pgBackRest repository directory
Create a dedicated directory for pgBackRest repositories with proper ownership and permissions.
sudo mkdir -p /var/lib/pgbackrest
sudo chown postgres:postgres /var/lib/pgbackrest
sudo chmod 750 /var/lib/pgbackrest
Configure pgBackRest
Create the main pgBackRest configuration file with local repository settings.
[global]
repo1-path=/var/lib/pgbackrest
repo1-retention-full=7
repo1-retention-diff=14
repo1-retention-archive=14
log-level-console=info
log-level-file=debug
log-path=/var/log/pgbackrest
log-timestamp=n
process-max=4
start-fast=y
stop-auto=y
archive-async=y
spool-path=/var/spool/pgbackrest
lock-path=/tmp/pgbackrest
[main]
pg1-path=/var/lib/postgresql/14/main
pg1-port=5432
pg1-socket-path=/var/run/postgresql
Create pgBackRest directories
Set up the required directories for logging and spooling with correct permissions.
sudo mkdir -p /var/log/pgbackrest
sudo mkdir -p /var/spool/pgbackrest
sudo chown postgres:postgres /var/log/pgbackrest
sudo chown postgres:postgres /var/spool/pgbackrest
sudo chmod 750 /var/log/pgbackrest
sudo chmod 750 /var/spool/pgbackrest
Configure PostgreSQL for archiving
Update PostgreSQL configuration to enable WAL archiving with pgBackRest.
# Add or modify these settings
wal_level = replica
archive_mode = on
archive_command = 'pgbackrest --stanza=main archive-push %p'
max_wal_senders = 3
wal_keep_size = 1024
Set pgBackRest configuration permissions
Secure the pgBackRest configuration file with appropriate ownership.
sudo chown postgres:postgres /etc/pgbackrest/pgbackrest.conf
sudo chmod 640 /etc/pgbackrest/pgbackrest.conf
Restart PostgreSQL
Restart PostgreSQL to apply the configuration changes.
sudo systemctl restart postgresql
sudo systemctl status postgresql
Initialize pgBackRest stanza
Create and initialize the backup stanza for your TimescaleDB instance.
sudo -u postgres pgbackrest --stanza=main stanza-create
Perform initial full backup
Run your first full backup to establish the baseline for incremental backups.
sudo -u postgres pgbackrest --stanza=main backup --type=full
Configure automated backup scheduling
Create a systemd timer for automated backups with proper service file.
[Unit]
Description=pgBackRest TimescaleDB Backup
Wants=postgresql.service
After=postgresql.service
[Service]
Type=oneshot
User=postgres
Group=postgres
ExecStart=/usr/bin/pgbackrest --stanza=main backup --type=diff
StandardOutput=journal
StandardError=journal
Create backup timer configuration
Set up the systemd timer to run daily differential backups.
[Unit]
Description=Run pgBackRest backup daily
Requires=pgbackrest-backup.service
[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
Enable and start backup timer
Enable the systemd timer to run automated backups.
sudo systemctl daemon-reload
sudo systemctl enable pgbackrest-backup.timer
sudo systemctl start pgbackrest-backup.timer
sudo systemctl status pgbackrest-backup.timer
Configure remote backup repository
Add S3-compatible remote repository
Configure pgBackRest to use a remote repository for off-site backups. This example uses MinIO but works with AWS S3 or any S3-compatible storage.
[global]
repo1-path=/var/lib/pgbackrest
repo1-retention-full=7
repo1-retention-diff=14
repo1-retention-archive=14
Add remote repository configuration
repo2-type=s3
repo2-s3-bucket=timescaledb-backups
repo2-s3-endpoint=s3.example.com
repo2-s3-key=your-access-key
repo2-s3-key-secret=your-secret-key
repo2-s3-region=us-east-1
repo2-retention-full=14
repo2-retention-diff=30
repo2-retention-archive=30
repo2-cipher-type=aes-256-cbc
repo2-cipher-pass=your-encryption-passphrase
log-level-console=info
log-level-file=debug
log-path=/var/log/pgbackrest
log-timestamp=n
process-max=4
start-fast=y
stop-auto=y
archive-async=y
spool-path=/var/spool/pgbackrest
lock-path=/tmp/pgbackrest
[main]
pg1-path=/var/lib/postgresql/14/main
pg1-port=5432
pg1-socket-path=/var/run/postgresql
Test remote repository
Verify the remote repository configuration works correctly.
sudo -u postgres pgbackrest --stanza=main --repo=2 check
Point-in-time recovery procedures
Stop PostgreSQL for recovery
Before performing any recovery operation, stop the PostgreSQL service.
sudo systemctl stop postgresql
Restore to latest backup
Restore your TimescaleDB instance to the most recent backup point.
sudo -u postgres pgbackrest --stanza=main restore
Restore to specific point in time
Restore to a specific timestamp using point-in-time recovery. Replace the timestamp with your desired recovery point.
sudo -u postgres pgbackrest --stanza=main --type=time --target="2024-01-15 14:30:00" restore
Start PostgreSQL after restore
Start PostgreSQL service after the restore operation completes.
sudo systemctl start postgresql
sudo systemctl status postgresql
Advanced backup configuration
Configure backup compression and encryption
Add compression and encryption to your backup configuration for security and storage efficiency.
[global]
Add compression settings
compress-type=lz4
compress-level=3
Add encryption for local repository
repo1-cipher-type=aes-256-cbc
repo1-cipher-pass=your-local-encryption-passphrase
Existing configuration continues...
Configure backup verification
Set up automatic backup verification to ensure backup integrity.
[Unit]
Description=pgBackRest Backup Verification
Wants=postgresql.service
After=postgresql.service
[Service]
Type=oneshot
User=postgres
Group=postgres
ExecStart=/usr/bin/pgbackrest --stanza=main check
StandardOutput=journal
StandardError=journal
Create weekly verification timer
Schedule weekly backup verification checks.
[Unit]
Description=Run pgBackRest check weekly
Requires=pgbackrest-check.service
[Timer]
OnCalendar=weekly
Persistent=true
RandomizedDelaySec=600
[Install]
WantedBy=timers.target
Enable verification timer
Enable the weekly backup verification timer.
sudo systemctl daemon-reload
sudo systemctl enable pgbackrest-check.timer
sudo systemctl start pgbackrest-check.timer
Verify your setup
sudo -u postgres pgbackrest --stanza=main info
sudo -u postgres pgbackrest --stanza=main check
sudo systemctl list-timers pgbackrest*
journalctl -u pgbackrest-backup.service -f
Monitor backup status
Create backup monitoring script
Set up a monitoring script to check backup status and send alerts.
#!/bin/bash
Check if latest backup is older than 25 hours
LATEST_BACKUP=$(sudo -u postgres pgbackrest --stanza=main info --output=json | jq -r '.[] | .backup[] | select(.type=="full" or .type=="diff") | .timestamp.stop' | sort | tail -1)
CURRENT_TIME=$(date +%s)
BACKUP_TIME=$(date -d "$LATEST_BACKUP" +%s)
DIFF=$(( (CURRENT_TIME - BACKUP_TIME) / 3600 ))
if [ $DIFF -gt 25 ]; then
echo "WARNING: Last backup is $DIFF hours old"
# Send alert (configure your notification method)
logger -t pgbackrest "WARNING: Last backup is $DIFF hours old"
exit 1
else
echo "OK: Last backup is $DIFF hours old"
exit 0
fi
Make monitoring script executable
Set proper permissions for the monitoring script.
sudo chmod +x /usr/local/bin/check-pgbackrest.sh
For advanced monitoring integration, you can link this with Grafana dashboards for TimescaleDB analytics or PostgreSQL monitoring with Prometheus.
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Archive command failed | Incorrect permissions or paths | Check /var/log/pgbackrest and verify postgres user has write access |
| Stanza creation fails | PostgreSQL not accessible | Verify pg1-path and socket path in /etc/pgbackrest/pgbackrest.conf |
| S3 backup fails | Invalid credentials or network | Test S3 connection: sudo -u postgres pgbackrest --stanza=main --repo=2 check |
| Restore hangs | Insufficient disk space | Check available space: df -h /var/lib/postgresql |
| Backup takes too long | Low process-max setting | Increase process-max in pgbackrest.conf based on CPU cores |
| Timer not running | Service dependencies missing | sudo systemctl status pgbackrest-backup.timer and check dependencies |
Next steps
- Set up TimescaleDB high availability with streaming replication
- Configure TimescaleDB automated data retention policies
- Set up backup monitoring with Prometheus alerts
- Implement comprehensive disaster recovery procedures
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration
PG_VERSION="${PG_VERSION:-14}"
DB_NAME="${1:-main}"
BACKUP_RETENTION_FULL="${BACKUP_RETENTION_FULL:-7}"
BACKUP_RETENTION_DIFF="${BACKUP_RETENTION_DIFF:-14}"
# Function definitions
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
usage() {
echo "Usage: $0 [stanza_name]"
echo " stanza_name: pgBackRest stanza name (default: main)"
exit 1
}
cleanup() {
log_error "Installation failed. Check logs above."
exit 1
}
trap cleanup ERR
# Check prerequisites
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root"
exit 1
fi
# Detect distribution
if [ ! -f /etc/os-release ]; then
log_error "/etc/os-release not found. Cannot detect distribution."
exit 1
fi
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update && apt upgrade -y"
PG_CONFIG_DIR="/etc/postgresql/$PG_VERSION/main"
PG_DATA_DIR="/var/lib/postgresql/$PG_VERSION/main"
PG_SERVICE="postgresql"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
PG_CONFIG_DIR="/var/lib/pgsql/$PG_VERSION/data"
PG_DATA_DIR="/var/lib/pgsql/$PG_VERSION/data"
PG_SERVICE="postgresql-$PG_VERSION"
;;
fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
PG_CONFIG_DIR="/var/lib/pgsql/data"
PG_DATA_DIR="/var/lib/pgsql/data"
PG_SERVICE="postgresql"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
PG_CONFIG_DIR="/var/lib/pgsql/data"
PG_DATA_DIR="/var/lib/pgsql/data"
PG_SERVICE="postgresql"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
log_info "Detected distribution: $ID"
log_info "PostgreSQL version: $PG_VERSION"
echo "[1/12] Updating system packages..."
$PKG_UPDATE
echo "[2/12] Installing pgBackRest..."
$PKG_INSTALL pgbackrest
echo "[3/12] Creating pgBackRest repository directory..."
mkdir -p /var/lib/pgbackrest
chown postgres:postgres /var/lib/pgbackrest
chmod 750 /var/lib/pgbackrest
echo "[4/12] Creating pgBackRest configuration directory..."
mkdir -p /etc/pgbackrest
echo "[5/12] Creating pgBackRest configuration file..."
cat > /etc/pgbackrest/pgbackrest.conf << EOF
[global]
repo1-path=/var/lib/pgbackrest
repo1-retention-full=$BACKUP_RETENTION_FULL
repo1-retention-diff=$BACKUP_RETENTION_DIFF
repo1-retention-archive=$BACKUP_RETENTION_DIFF
log-level-console=info
log-level-file=debug
log-path=/var/log/pgbackrest
log-timestamp=n
process-max=4
start-fast=y
stop-auto=y
archive-async=y
spool-path=/var/spool/pgbackrest
lock-path=/tmp/pgbackrest
[$DB_NAME]
pg1-path=$PG_DATA_DIR
pg1-port=5432
EOF
# Add socket path for Debian-based systems
if [[ "$ID" == "ubuntu" || "$ID" == "debian" ]]; then
echo "pg1-socket-path=/var/run/postgresql" >> /etc/pgbackrest/pgbackrest.conf
fi
echo "[6/12] Creating pgBackRest directories..."
mkdir -p /var/log/pgbackrest /var/spool/pgbackrest
chown postgres:postgres /var/log/pgbackrest /var/spool/pgbackrest
chmod 750 /var/log/pgbackrest /var/spool/pgbackrest
echo "[7/12] Configuring PostgreSQL for archiving..."
PG_CONF="$PG_CONFIG_DIR/postgresql.conf"
if [ ! -f "$PG_CONF" ]; then
log_error "PostgreSQL configuration file not found at $PG_CONF"
log_error "Please ensure PostgreSQL $PG_VERSION is installed"
exit 1
fi
# Backup original config
cp "$PG_CONF" "$PG_CONF.backup.$(date +%Y%m%d_%H%M%S)"
# Configure PostgreSQL parameters
sed -i "s/#*wal_level = .*/wal_level = replica/" "$PG_CONF"
sed -i "s/#*archive_mode = .*/archive_mode = on/" "$PG_CONF"
sed -i "s/#*archive_command = .*/archive_command = 'pgbackrest --stanza=$DB_NAME archive-push %p'/" "$PG_CONF"
sed -i "s/#*max_wal_senders = .*/max_wal_senders = 3/" "$PG_CONF"
# Add parameters if they don't exist
if ! grep -q "^wal_level" "$PG_CONF"; then
echo "wal_level = replica" >> "$PG_CONF"
fi
if ! grep -q "^archive_mode" "$PG_CONF"; then
echo "archive_mode = on" >> "$PG_CONF"
fi
if ! grep -q "^archive_command" "$PG_CONF"; then
echo "archive_command = 'pgbackrest --stanza=$DB_NAME archive-push %p'" >> "$PG_CONF"
fi
if ! grep -q "^max_wal_senders" "$PG_CONF"; then
echo "max_wal_senders = 3" >> "$PG_CONF"
fi
# Add wal_keep_size for PostgreSQL 13+
if [[ $PG_VERSION -ge 13 ]]; then
if ! grep -q "^wal_keep_size" "$PG_CONF"; then
echo "wal_keep_size = 1024MB" >> "$PG_CONF"
fi
fi
echo "[8/12] Setting pgBackRest configuration permissions..."
chown postgres:postgres /etc/pgbackrest/pgbackrest.conf
chmod 640 /etc/pgbackrest/pgbackrest.conf
echo "[9/12] Restarting PostgreSQL..."
systemctl restart $PG_SERVICE
systemctl enable $PG_SERVICE
sleep 5
if ! systemctl is-active --quiet $PG_SERVICE; then
log_error "PostgreSQL failed to start. Check logs: journalctl -u $PG_SERVICE"
exit 1
fi
echo "[10/12] Initializing pgBackRest stanza..."
sudo -u postgres pgbackrest --stanza=$DB_NAME stanza-create
echo "[11/12] Performing initial full backup..."
sudo -u postgres pgbackrest --stanza=$DB_NAME backup --type=full
echo "[12/12] Setting up automated backup scheduling..."
cat > /etc/systemd/system/pgbackrest-backup.service << EOF
[Unit]
Description=pgBackRest TimescaleDB Backup
Wants=$PG_SERVICE.service
After=$PG_SERVICE.service
[Service]
Type=oneshot
User=postgres
Group=postgres
ExecStart=/usr/bin/pgbackrest --stanza=$DB_NAME backup --type=diff
StandardOutput=journal
StandardError=journal
EOF
cat > /etc/systemd/system/pgbackrest-backup.timer << EOF
[Unit]
Description=Run pgBackRest backup daily
Requires=pgbackrest-backup.service
[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable pgbackrest-backup.timer
systemctl start pgbackrest-backup.timer
# Verification
echo
log_info "Verifying installation..."
if sudo -u postgres pgbackrest --stanza=$DB_NAME info > /dev/null 2>&1; then
log_info "✓ pgBackRest stanza created successfully"
else
log_error "✗ pgBackRest stanza verification failed"
fi
if systemctl is-active --quiet pgbackrest-backup.timer; then
log_info "✓ Backup timer is active"
else
log_warn "✗ Backup timer is not active"
fi
echo
log_info "pgBackRest installation completed successfully!"
log_info "Configuration file: /etc/pgbackrest/pgbackrest.conf"
log_info "Repository path: /var/lib/pgbackrest"
log_info "Log path: /var/log/pgbackrest"
log_info ""
log_info "Common commands:"
log_info " Check backup info: sudo -u postgres pgbackrest --stanza=$DB_NAME info"
log_info " Manual full backup: sudo -u postgres pgbackrest --stanza=$DB_NAME backup --type=full"
log_info " Manual diff backup: sudo -u postgres pgbackrest --stanza=$DB_NAME backup --type=diff"
log_info " Timer status: systemctl status pgbackrest-backup.timer"
Review the script before running. Execute with: bash install.sh