The Ops-Center includes an integrated PostgreSQL database backup system with automated scheduling, retention policies, and restore capabilities.
- Automated Backups: Scheduled backups run automatically at configurable intervals
- Compression: Backups are gzip-compressed to save storage space
- Retention Policy: Automatic cleanup based on age and count limits
- REST API: Full API for backup management
- CLI Tool: Command-line interface for manual operations
- Metadata Tracking: JSON metadata for each backup with size, timestamp, and description
Add these to your .env file:
# Backup directory
BACKUP_DIR=/app/backups/database
# Retention settings
BACKUP_RETENTION_DAYS=7 # Keep backups for 7 days
BACKUP_MAX_COUNT=30 # Keep maximum 30 backups
# Automation
BACKUP_INTERVAL_HOURS=24 # Run backup every 24 hours
BACKUP_ENABLED=true # Enable scheduled backupsAdd a volume mount to your docker-compose.yml to persist backups:
services:
ops-center:
volumes:
# ... existing volumes ...
- backup-data:/app/backups
volumes:
backup-data:
driver: localFor external/persistent storage:
volumes:
backup-data:
driver: local
driver_opts:
type: none
o: bind
device: /path/to/backup/storageThe backup API is available at /api/backups:
curl -X POST http://localhost:3001/api/backups \
-H "Content-Type: application/json" \
-d '{"description": "Manual backup before upgrade"}'curl http://localhost:3001/api/backupscurl -X POST http://localhost:3001/api/backups/restore \
-H "Content-Type: application/json" \
-d '{"backup_filename": "backup_unicorn_db_20260129_120000.sql.gz"}'curl -X DELETE http://localhost:3001/api/backups/backup_unicorn_db_20260129_120000.sql.gzcurl http://localhost:3001/api/backups/statuscurl -X POST http://localhost:3001/api/backups/cleanupThe CLI tool provides a simple command-line interface:
# Simple backup
./backup-database.sh create
# With description
./backup-database.sh create --description "Before major update"
# Or use Python directly
python3 backend/backup_cli.py create -d "My backup"./backup-database.sh listOutput:
📦 Available Backups (5):
────────────────────────────────────────────────────────────────────────────────
Filename Size Created Description
────────────────────────────────────────────────────────────────────────────────
backup_unicorn_db_20260129_143022.sql.gz 15.32 MB 2026-01-29 14:30:22 Before upgrade
backup_unicorn_db_20260129_120000.sql.gz 14.87 MB 2026-01-29 12:00:00 Automated scheduled backup
...
────────────────────────────────────────────────────────────────────────────────
Total: 5 backups, 72.45 MB
./backup-database.sh restore backup_unicorn_db_20260129_120000.sql.gzYou'll be prompted to confirm:
⚠️ WARNING: This will restore the database from backup!
Backup file: backup_unicorn_db_20260129_120000.sql.gz
Type 'yes' to continue:
./backup-database.sh delete backup_unicorn_db_20260129_120000.sql.gzManually trigger cleanup based on retention policy:
./backup-database.sh cleanupIf running in Docker, you can execute commands inside the container:
# Create backup
docker exec ops-center python3 backend/backup_cli.py create
# List backups
docker exec ops-center python3 backend/backup_cli.py list
# Restore backup
docker exec ops-center python3 backend/backup_cli.py restore <filename>The backup service automatically starts when the application launches and runs backups according to BACKUP_INTERVAL_HOURS.
- Interval: Every 24 hours (configurable)
- First Run: 24 hours after startup
- Description: "Automated scheduled backup"
Check application logs for backup activity:
# View logs
docker logs -f ops-center | grep -i backup
# Expected output
[INFO] Database Backup Service initialized (interval: 24h)
[INFO] Running scheduled backup...
[INFO] ✅ Backup created successfully: backup_unicorn_db_20260129_120000.sql.gz (14.87 MB)
[INFO] ✅ Scheduled backup completed: backup_unicorn_db_20260129_120000.sql.gzBackups are stored with the following structure:
/app/backups/database/
├── backup_unicorn_db_20260129_143022.sql.gz # Compressed SQL dump
├── backup_unicorn_db_20260129_143022.sql.gz.json # Metadata
├── backup_unicorn_db_20260129_120000.sql.gz
└── backup_unicorn_db_20260129_120000.sql.gz.json
{
"filename": "backup_unicorn_db_20260129_143022.sql.gz",
"timestamp": "20260129_143022",
"created_at": "2026-01-29T14:30:22.123456",
"database": "unicorn_db",
"size_bytes": 16070144,
"size_mb": 15.32,
"description": "Before major update",
"compressed": true
}Backups are automatically cleaned up based on:
- Age: Older than
BACKUP_RETENTION_DAYS(default: 7 days) - Count: Exceeds
BACKUP_MAX_COUNT(default: 30 backups)
Cleanup runs:
- After each backup creation
- When manually triggered via API or CLI
Note: The system keeps backups that meet either criteria, so you'll always have the most recent BACKUP_MAX_COUNT backups, even if they're older than the retention period.
./backup-database.sh create --description "Before v2.0 upgrade"# Check backup status
curl http://localhost:3001/api/backups/status
# List recent backups
./backup-database.sh list | head -n 15-
Keep External Copies: Copy critical backups to external storage
docker cp ops-center:/app/backups/database/backup_*.sql.gz ./external-backup/ -
Test Restores: Periodically test restore procedures in staging
# Staging environment ./backup-database.sh restore backup_unicorn_db_YYYYMMDD_HHMMSS.sql.gz -
Monitor Disk Space: Ensure adequate storage for backups
df -h /app/backups
- Database migrations
- Major version upgrades
- Data cleanup operations
- Configuration changes
Error: pg_dump: error: connection to server failed
Solution: Check database connection settings:
echo $POSTGRES_HOST
echo $POSTGRES_PORTError: OSError: [Errno 28] No space left on device
Solution:
- Reduce
BACKUP_RETENTION_DAYSorBACKUP_MAX_COUNT - Run cleanup manually:
./backup-database.sh cleanup - Add more storage to backup volume
Error: psql: error: connection refused
Solution: Ensure database is running and accessible
Error: Permission denied: '/app/backups/database'
Solution: Check volume permissions or run with appropriate user
- Backup Encryption: Consider encrypting backups for sensitive data
- Access Control: Restrict API access to authorized users
- Storage Security: Ensure backup directory has appropriate permissions
- Off-site Backups: Maintain copies outside the primary server
{
"success": true,
"message": "Backup created successfully",
"backup": {
"filename": "backup_unicorn_db_20260129_143022.sql.gz",
"timestamp": "20260129_143022",
"created_at": "2026-01-29T14:30:22.123456",
"size_mb": 15.32,
"description": "Manual backup"
}
}[
{
"filename": "backup_unicorn_db_20260129_143022.sql.gz",
"size_mb": 15.32,
"created_at": "2026-01-29T14:30:22.123456",
"description": "Before upgrade",
"compressed": true
}
]{
"enabled": true,
"backup_directory": "/app/backups/database",
"retention_days": 7,
"max_backups": 30,
"interval_hours": 24,
"total_backups": 5
}Add to your deployment pipeline:
- name: Create Pre-Deployment Backup
run: |
curl -X POST http://ops-center:3001/api/backups \
-H "Content-Type: application/json" \
-d '{"description": "Pre-deployment backup - Build ${{ github.run_number }}"}'- name: Verify Backup System
run: |
status=$(curl -s http://ops-center:3001/api/backups/status)
echo $status | jq '.enabled' | grep -q true