db-backup.sh
A comprehensive PostgreSQL backup and restore utility that works with Dockerised databases.
- Backup: Create compressed or uncompressed database backups
- Restore: Restore databases from backup files
- List: View all available backups with size and date
- Clean-up: Automatically remove old backups based on retention policy
- Docker Integration: Works seamlessly with Docker secrets and containers
- Compression: Automatic gzip compression to save storage space
- Safety: Confirmation prompts for destructive operations
# Create a backup
./infra/scripts/db-backup.sh backup
# List all backups
./infra/scripts/db-backup.sh list
# Restore from a backup
./infra/scripts/db-backup.sh restore --file storage/backups/oullin_db_20260102_153045.sql.gz
# Clean up old backups (keeps last 7 days by default)
./infra/scripts/db-backup.sh cleanupYou can use the Makefile shortcuts defined in infra/makefile/backup.mk:
# Create a backup
make backup:create
# List backups
make backup:list
# Restore from a backup
make backup:restore file=storage/backups/oullin_db_20260102_153045.sql.gz
# Cleanup old backups (default retention: 7 days)
make backup:cleanup
# Cleanup with a custom retention period
make BACKUP_RETENTION_DAYS=14 backup:cleanup
# Setup cron-based backups (weekly Sundays at 2 AM)
make backup:cron:setup
# Setup cron-based backups with a custom schedule
make backup:cron:setup schedule="0 2 * * *"
# Preview cron changes
make backup:cron:setup:dry-run
# Show or remove cron jobs
make backup:cron:show
make backup:cron:removeCreate a new database backup.
# Create a compressed backup (default)
./infra/scripts/db-backup.sh backup
# Create an uncompressed backup
./infra/scripts/db-backup.sh --compress false backupBackups are stored in storage/backups/ by default with the naming format:
oullin_db_YYYYMMDD_HHMMSS.sql.gz
Restore the database from a backup file.
./infra/scripts/db-backup.sh restore --file storage/backups/oullin_db_20260102_153045.sql.gzWarning: This will replace all data in the current database. A confirmation prompt will be shown.
Display all available backups with their size and creation date.
./infra/scripts/db-backup.sh listRemove backups older than the retention period (default: 7 days).
# Use default retention (7 days)
./infra/scripts/db-backup.sh cleanup
# Specify custom retention period
./infra/scripts/db-backup.sh --retention 30 cleanup| Option | Description | Default |
|---|---|---|
-h, --help |
Show help message | - |
-f, --file FILE |
Backup file to restore (required for restore) | - |
-c, --compress BOOL |
Compress backup with gzip | true |
-r, --retention DAYS |
Number of days to keep backups | 7 |
-d, --dir DIR |
Backup directory | storage/backups |
You can customise the script behaviour using environment variables:
# Custom backup directory
export BACKUP_DIR="/path/to/backups"
./infra/scripts/db-backup.sh backup
# Custom database container name
export DB_CONTAINER_NAME="my_postgres_container"
./infra/scripts/db-backup.sh backup
# Custom retention period
export BACKUP_RETENTION_DAYS=30
./infra/scripts/db-backup.sh cleanupTo schedule automated backups, add a cron job:
# Edit crontab
crontab -e
# Add daily backup at 2 AM
0 2 * * * cd /path/to/oullin/api && ./infra/scripts/db-backup.sh backup >> /var/log/db-backup.log 2>&1
# Add weekly cleanup on Sundays at 3 AM
0 3 * * 0 cd /path/to/oullin/api && ./infra/scripts/db-backup.sh cleanup >> /var/log/db-backup.log 2>&1For a more robust setup with proper logging and error handling, see the example in setup-cron-backup.sh.
On VPS servers, use the Makefile helper so it wires up logging and cleanup for you:
# SSH into the server and move into the repo
cd /path/to/oullin/api
# Install weekly backups + cleanup (default retention: 7 days)
make backup:cron:setup
# Or install a daily backup at 2 AM with 14-day retention
make BACKUP_RETENTION_DAYS=14 backup:cron:setup schedule="0 2 * * *"
# Verify cron entries
make backup:cron:show
# Check logs
tail -f storage/logs/db-backup.logThe cron jobs created by setup-cron-backup.sh run backups and cleanup from the repo root and log to storage/logs/db-backup.log.
Prereqs on VPS:
cron(orcronie) installed and enableddockerand the database container running
By default, backups are stored in:
storage/backups/
├── oullin_db_20260102_020000.sql.gz
├── oullin_db_20260103_020000.sql.gz
└── oullin_db_20260104_020000.sql.gz
You can change this location using the --dir option or BACKUP_DIR environment variable.
The script uses pg_dump with the following options:
--format=plain: SQL text format (easy to inspect and edit)--no-owner: Don't include ownership commands--no-privileges: Don't include privilege commands--no-acl: Don't include ACL commands
This makes backups portable and easy to restore on different systems.
- Credentials: The script reads database credentials from Docker secrets, ensuring sensitive data isn't exposed
- Backups: Store backups in a secure location with appropriate permissions
- Retention: Configure appropriate retention periods to balance storage costs and recovery needs
- Encryption: Consider encrypting backups for production environments
- Off-site: For production, implement off-site backup storage (S3, etc.)
[ERROR] Database container 'oullin_db' is not running
Start the database container:
docker compose up -d api-dbpermission denied: ./infra/scripts/db-backup.sh
Make the script executable:
chmod +x infra/scripts/db-backup.sh[ERROR] Failed to read database credentials from Docker secrets
Ensure the container is running and secrets are properly configured in docker-compose.yml.
# Create a backup
./infra/scripts/db-backup.sh backup
# Verify the backup was created
./infra/scripts/db-backup.sh list
# Test restore in a development environment
./infra/scripts/db-backup.sh restore --file storage/backups/oullin_db_20260102_153045.sql.gz# 1. List available backups
./infra/scripts/db-backup.sh list
# 2. Restore from the most recent backup
./infra/scripts/db-backup.sh restore --file storage/backups/oullin_db_20260104_020000.sql.gz
# 3. Verify the restoration
docker exec oullin_db psql -U <your_username> -d oullin_db -c "SELECT COUNT(*) FROM your_table;"You can fetch the database username from Docker secrets inside the container:
docker exec oullin_db cat /run/secrets/pg_username# On old server: Create backup
./infra/scripts/db-backup.sh backup
# Copy backup to new server
scp storage/backups/oullin_db_20260102_153045.sql.gz user@newserver:/path/to/oullin/api/storage/backups/
# On new server: Restore
./infra/scripts/db-backup.sh restore --file storage/backups/oullin_db_20260102_153045.sql.gzThe backup script can be integrated into your deployment pipeline:
# Example GitHub Actions workflow
- name: Backup database before deployment
run: |
./infra/scripts/db-backup.sh backup
- name: Deploy application
run: |
# Your deployment commands
- name: Cleanup old backups
run: |
./infra/scripts/db-backup.sh cleanup --retention 30