Two Postgres databases on Hetzner with zero backup:
Automated pg_dump → restic → Backblaze B2. Encrypted, deduplicated, incremental.
apt-get install -y restic
Shai provides:
struxio-db-backupsexport B2_ACCOUNT_ID="<from Shai>"
export B2_ACCOUNT_KEY="<from Shai>"
restic -r b2:struxio-db-backups init
Save the restic repo password in SOPS-encrypted file.
#!/bin/bash
set -euo pipefail
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
DUMP_DIR=/tmp/struxio-db-dumps
mkdir -p "$DUMP_DIR"
# Source credentials
source /opt/struxio/scripts/.backup_env
# Dump Bus DB
docker exec deploy-postgres-1 pg_dump -U bus -d bus --clean --if-exists | gzip > "$DUMP_DIR/bus_${TIMESTAMP}.sql.gz"
# Dump Paperclip DB
docker exec deploy-paperclip-db-1 pg_dump -U paperclip -d paperclip --clean --if-exists | gzip > "$DUMP_DIR/paperclip_${TIMESTAMP}.sql.gz"
# Backup to B2 via restic
restic -r b2:struxio-db-backups backup "$DUMP_DIR/" --tag "db-backup" --tag "$TIMESTAMP"
# Prune old backups: keep 7 daily, 4 weekly, 3 monthly
restic -r b2:struxio-db-backups forget --keep-daily 7 --keep-weekly 4 --keep-monthly 3 --prune
# Cleanup local dumps
rm -rf "$DUMP_DIR"
echo "[$(date)] Backup complete: $TIMESTAMP" >> /opt/struxio/logs/backup.log
# /opt/struxio/scripts/.backup_env (plaintext, chmod 600)
export B2_ACCOUNT_ID="xxx"
export B2_ACCOUNT_KEY="xxx"
export RESTIC_PASSWORD="xxx"
export RESTIC_REPOSITORY="b2:struxio-db-backups"
Encrypt with SOPS, decrypt at runtime.
# /etc/cron.d/struxio-db-backup
0 6,18 * * * struxio /opt/struxio/scripts/backup_databases.sh >> /opt/struxio/logs/backup.log 2>&1
restic snapshots to listrestic restore latest --target /tmp/restore-test| Period | Keep |
|---|---|
| Daily | 7 days |
| Weekly | 4 weeks |
| Monthly | 3 months |
STRUXIO.ai // Confidential & Proprietary // © 2026