PostgreSQL Agent

Standalone backup agent for PostgreSQL databases. Supports logical dumps, base backups, and continuous WAL archiving for point-in-time recovery.

v1.0.0Updated 2026-01-01

Features

  • PostgreSQL 12, 13, 14, 15, 16+ support
  • Logical backups (pg_dump) and physical backups (pg_basebackup)
  • Continuous WAL archiving for point-in-time recovery
  • Custom and directory format support
  • Parallel dump and restore
  • Schema-only and data-only backup options
  • Automatic source identifier for backup tracking

Quick Start

Docker Compose

# docker-compose.yml
version: '3.8'

services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: myapp
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: myapp
    volumes:
      - pg_data:/var/lib/postgresql/data

  lox-pg-agent:
    image: loxbackup/postgresql-agent:latest
    environment:
      LOX_API_KEY: ${LOX_API_KEY}
      PGHOST: postgres
      PGPORT: 5432
      PGUSER: myapp
      PGPASSWORD: ${POSTGRES_PASSWORD}
      PGDATABASE: myapp
      BACKUP_SCHEDULE: "0 3 * * *"
      BACKUP_NAME: "myapp-postgres"
    depends_on:
      - postgres

volumes:
  pg_data:

Standalone Binary

# Download the agent
curl -L https://releases.backlox.com/postgresql-agent/latest/linux-amd64 -o lox-pg-agent
chmod +x lox-pg-agent

# Configure
export LOX_API_KEY="lox_xxxxxxxxxxxx"
export PGHOST="localhost"
export PGUSER="backup_user"
export PGPASSWORD="secure_password"
export PGDATABASE="myapp"

# Run a backup
./lox-pg-agent backup

# Or run as daemon with schedule
./lox-pg-agent daemon --schedule "0 3 * * *"

Environment Variables

Connection Settings

VariableDefaultDescription
PGHOSTlocalhostPostgreSQL server hostname
PGPORT5432PostgreSQL server port
PGUSERpostgresPostgreSQL username
PGPASSWORD-PostgreSQL password (required)
PGDATABASEallSpecific database or "all" for all databases
PGSSLMODEpreferSSL mode: disable, allow, prefer, require, verify-ca, verify-full

Backup Settings

VariableDefaultDescription
LOX_API_KEY-LOX API key (required)
BACKUP_NAMEpostgres-backupBackup name prefix
BACKUP_SCHEDULE0 3 * * *Cron schedule expression
BACKUP_RETENTION30Days to retain backups
BACKUP_TYPElogicalBackup type: logical or physical
BACKUP_FORMATcustomFormat: custom, directory, plain, tar
SOURCE_IDENTIFIERautoCustom source ID (auto-generated if not set)

Source Identifier

The agent automatically generates a unique source_identifier for each database instance (format: postgresql-hostname-database-hash). This enables tracking backups across multiple servers and identifying their origin in the dashboard.

Advanced Options

VariableDefaultDescription
COMPRESSIONgzipCompression: gzip, zstd, lz4, or none
PARALLEL_JOBS4Number of parallel dump/restore jobs
SCHEMA_ONLYfalseDump schema only (no data)
DATA_ONLYfalseDump data only (no schema)
EXCLUDE_SCHEMAS-Comma-separated schemas to exclude
EXCLUDE_TABLES-Comma-separated tables to exclude
INCLUDE_BLOBStrueInclude large objects in dump
PG_DUMP_EXTRA_ARGS-Additional pg_dump arguments

Database User Setup

Create a dedicated backup user with minimum required privileges:

-- For logical backups (pg_dump)
CREATE ROLE lox_backup WITH LOGIN PASSWORD 'secure_password';
GRANT CONNECT ON DATABASE myapp TO lox_backup;
GRANT USAGE ON SCHEMA public TO lox_backup;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO lox_backup;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO lox_backup;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO lox_backup;

-- For backing up all databases
ALTER ROLE lox_backup WITH SUPERUSER;  -- Or use pg_read_all_data role in PG14+

-- PostgreSQL 14+ alternative (more secure)
GRANT pg_read_all_data TO lox_backup;

-- For physical backups (pg_basebackup)
ALTER ROLE lox_backup WITH REPLICATION;

pg_hba.conf Configuration

Ensure the backup user can connect from the agent container:

# TYPE  DATABASE  USER        ADDRESS         METHOD
host    all       lox_backup  172.0.0.0/8     scram-sha-256
host    replication lox_backup 172.0.0.0/8    scram-sha-256

Backup Types

Logical Backup (Default)

Uses pg_dump or pg_dumpall to create logical dumps.

FormatDescriptionUse Case
customCompressed, flexible restoreRecommended for most cases
directoryParallel dump, per-table filesLarge databases, parallel restore
plainPlain SQL textSimple restore, readability
tarTar archive formatCompatibility with older tools

Physical Backup

Uses pg_basebackup for hot physical backups. Best for large databases requiring fast recovery.

lox-pg-agent:
  image: loxbackup/postgresql-agent:latest
  environment:
    LOX_API_KEY: ${LOX_API_KEY}
    PGHOST: postgres
    PGPASSWORD: ${POSTGRES_PASSWORD}
    BACKUP_TYPE: physical
    BACKUP_SCHEDULE: "0 2 * * *"

Point-in-Time Recovery (PITR)

Enable WAL archiving for continuous backup and point-in-time recovery:

lox-pg-agent:
  image: loxbackup/postgresql-agent:latest
  environment:
    LOX_API_KEY: ${LOX_API_KEY}
    PGHOST: postgres
    PGPASSWORD: ${POSTGRES_PASSWORD}
    BACKUP_TYPE: physical
    # Enable WAL archiving
    WAL_ARCHIVING: "true"
    WAL_ARCHIVE_SCHEDULE: "*/5 * * * *"  # Every 5 minutes
    # Full backup weekly
    BACKUP_SCHEDULE: "0 3 * * 0"

PostgreSQL Server Configuration

Enable WAL archiving in your postgresql.conf:

wal_level = replica
archive_mode = on
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'
max_wal_senders = 3

Examples

Single Database with Parallel Dump

lox-pg-agent:
  image: loxbackup/postgresql-agent:latest
  environment:
    LOX_API_KEY: ${LOX_API_KEY}
    PGHOST: postgres
    PGPASSWORD: ${POSTGRES_PASSWORD}
    PGDATABASE: myapp
    BACKUP_FORMAT: directory
    PARALLEL_JOBS: 8  # Use 8 parallel workers
    BACKUP_NAME: "myapp-parallel"
    BACKUP_SCHEDULE: "0 */4 * * *"

Exclude Large Tables

lox-pg-agent:
  image: loxbackup/postgresql-agent:latest
  environment:
    LOX_API_KEY: ${LOX_API_KEY}
    PGHOST: postgres
    PGPASSWORD: ${POSTGRES_PASSWORD}
    PGDATABASE: analytics
    # Skip large audit/log tables
    EXCLUDE_TABLES: "public.audit_log,public.events,public.metrics_raw"
    BACKUP_NAME: "analytics-db"

Schema-Only Backup

lox-pg-agent:
  image: loxbackup/postgresql-agent:latest
  environment:
    LOX_API_KEY: ${LOX_API_KEY}
    PGHOST: postgres
    PGPASSWORD: ${POSTGRES_PASSWORD}
    PGDATABASE: myapp
    SCHEMA_ONLY: "true"
    BACKUP_NAME: "myapp-schema"
    BACKUP_SCHEDULE: "0 0 * * *"  # Daily at midnight

Amazon RDS / Aurora PostgreSQL

lox-pg-agent:
  image: loxbackup/postgresql-agent:latest
  environment:
    LOX_API_KEY: ${LOX_API_KEY}
    PGHOST: mydb.xxxx.us-east-1.rds.amazonaws.com
    PGPORT: 5432
    PGUSER: postgres
    PGPASSWORD: ${RDS_PASSWORD}
    PGDATABASE: myapp
    PGSSLMODE: require
    # RDS doesn't allow physical backups
    BACKUP_TYPE: logical
    BACKUP_FORMAT: custom
    BACKUP_NAME: "rds-postgres-prod"

All Databases Backup

lox-pg-agent:
  image: loxbackup/postgresql-agent:latest
  environment:
    LOX_API_KEY: ${LOX_API_KEY}
    PGHOST: postgres
    PGUSER: postgres
    PGPASSWORD: ${POSTGRES_PASSWORD}
    PGDATABASE: all  # Backup all databases
    BACKUP_NAME: "postgres-cluster"
    BACKUP_SCHEDULE: "0 3 * * *"

Restoring Backups

Restore from Custom Format

# Download the backup
lox backup download <backup-id> -o backup.dump

# Restore to PostgreSQL
pg_restore -h localhost -U postgres -d myapp backup.dump

# Restore with parallel jobs
pg_restore -h localhost -U postgres -d myapp -j 4 backup.dump

# Restore specific tables only
pg_restore -h localhost -U postgres -d myapp -t users -t orders backup.dump

Restore from Plain Format

# Download the backup
lox backup download <backup-id> -o backup.sql.gz

# Decompress and restore
gunzip backup.sql.gz
psql -h localhost -U postgres -d myapp < backup.sql

Point-in-Time Recovery

# 1. Stop PostgreSQL
sudo systemctl stop postgresql

# 2. Clear data directory
rm -rf /var/lib/postgresql/16/main/*

# 3. Download and extract base backup
lox backup download <base-backup-id> -o base.tar.gz
tar -xzf base.tar.gz -C /var/lib/postgresql/16/main/

# 4. Download WAL archives
lox backup download <wal-backup-id> -o wal.tar.gz
tar -xzf wal.tar.gz -C /var/lib/postgresql/16/main/pg_wal/

# 5. Create recovery signal with target time
cat > /var/lib/postgresql/16/main/recovery.signal << EOF
EOF

# 6. Configure recovery target in postgresql.conf
echo "recovery_target_time = '2024-01-15 14:30:00'" >> postgresql.conf
echo "restore_command = 'cp /path/to/wal/%f %p'" >> postgresql.conf

# 7. Start PostgreSQL
sudo systemctl start postgresql

Monitoring

The agent exposes Prometheus metrics and health endpoints:

EndpointDescription
/healthLiveness check
/readyReadiness check (DB connected)
/metricsPrometheus metrics

Key Metrics

# Backup metrics
lox_pg_backup_total{status="success"}
lox_pg_backup_total{status="failed"}
lox_pg_backup_duration_seconds
lox_pg_backup_size_bytes
lox_pg_last_backup_timestamp

# WAL metrics
lox_pg_wal_archived_total
lox_pg_wal_archive_lag_bytes

# Database metrics
lox_pg_database_size_bytes{database="myapp"}
lox_pg_tables_count{database="myapp"}

Troubleshooting

FATAL: password authentication failed

Verify password is correct and pg_hba.conf allows the connection. Check PGSSLMODE matches server configuration.

pg_dump: permission denied

The backup user needs SELECT permission on all tables. Use pg_read_all_data role in PostgreSQL 14+ or grant SUPERUSER for earlier versions.

pg_basebackup: could not connect

Physical backups require REPLICATION privilege and a replication entry in pg_hba.conf. Ensure max_wal_senders is set high enough.

Debug mode

Set LOG_LEVEL: debug to see detailed pg_dump commands and output.