Automated backups, remote storage, point-in-time recovery, and safe restore procedures.
nself backup create # Create backup now
nself backup list # List all backups
nself backup restore <id> # Restore from backup ID
nself backup restore latest # Restore from most recent backup
nself backup verify # Verify backup integrity
nself backup verify <id> # Verify specific backup
nself backup prune # Remove expired backups
nself backup status # Show backup system health
nself backup schedule # Open schedule confignself backup create
# Output:
# Creating backup... done
# Backup ID: bak_20260507_143022
# Database: 42 MB
# Files: 128 MB (storage objects)
# Total: 170 MB
# Location: /var/nself/backups/bak_20260507_143022
# Encrypted: yes (AES-256)
# Label a backup (useful before major changes)
nself backup create --label pre-migration
nself backup create --label before-v1.1.0-upgrade.environments/prod/.env, excluding secrets)nself backup list
# Output:
# ID Label Created Size Status Remote
# bak_20260507_143022 pre-migration 2026-05-07 14:30 170 MB ok ✓
# bak_20260507_020000 auto 2026-05-07 02:00 169 MB ok ✓
# bak_20260506_020000 auto 2026-05-06 02:00 168 MB ok ✓
# Show only remote backups
nself backup list --remote
# Show only local backups
nself backup list --local# In .environments/prod/.env:
BACKUP_ENABLED=true
BACKUP_SCHEDULE=0 2 * * * # Daily at 2 AM UTC (cron syntax)
BACKUP_RETENTION_DAYS=30 # Keep 30 days of local backups
BACKUP_ENCRYPT=true
BACKUP_ENCRYPT_KEY=<openssl rand -hex 32>
# Reload to apply schedule changes
nself service reload backup-agent| Usage | Schedule | Retention |
|---|---|---|
| Personal / low-traffic | Daily at 2 AM | 30 days |
| Team / moderate traffic | Every 6 hours | 14 days |
| High-traffic / business critical | Hourly + WAL archiving | 7 days local + 90 days remote |
Remote backups protect against server loss. ɳSelf supports S3, Cloudflare R2, Google Cloud Storage, Azure Blob, and Backblaze B2.
BACKUP_REMOTE=r2
BACKUP_R2_BUCKET=your-backup-bucket
BACKUP_R2_ACCOUNT_ID=your-cloudflare-account-id
BACKUP_R2_ACCESS_KEY=your-r2-access-key
BACKUP_R2_SECRET_KEY=your-r2-secret-keyBACKUP_REMOTE=s3
BACKUP_S3_BUCKET=your-backup-bucket
BACKUP_S3_REGION=us-east-1
BACKUP_S3_ACCESS_KEY=AKIA...
BACKUP_S3_SECRET_KEY=...
# Enable S3 Object Lock for immutable backups (ransomware protection)
BACKUP_S3_OBJECT_LOCK=trueBACKUP_REMOTE=gcs
BACKUP_GCS_BUCKET=your-backup-bucket
BACKUP_GCS_PROJECT=your-gcp-project
BACKUP_GCS_CREDENTIALS=/path/to/service-account.jsonWarning: Restore replaces the current database entirely. Data written after the backup point is lost. Always create a fresh backup before restoring — even from a bad state — in case you need to undo the restore.
# Restore latest backup (with interactive confirmation)
nself backup restore latest
# Restore specific backup
nself backup restore bak_20260506_020000
# Restore from remote (if local copy missing)
nself backup restore bak_20260506_020000 --from-remote
# Skip confirmation (for automation)
nself backup restore bak_20260506_020000 --yes
# Output:
# Creating safety snapshot... done (bak_20260507_pre-restore)
# Stopping services...
# Restoring database... done (42 MB, 8.3s)
# Restoring files... done (128 MB, 12.1s)
# Running post-restore migrations... done
# Starting services...
# Health check... all services healthy
# Restore complete.# Requires WAL archiving enabled:
BACKUP_WAL_ENABLED=true
BACKUP_WAL_S3_BUCKET=your-wal-archive-bucket
# Restore to a specific moment
nself backup restore --pitr "2026-05-07 13:45:00 UTC"
# View WAL archive coverage (what time range is restorable)
nself backup wal-coverage# Verify most recent backup
nself backup verify
# Verify specific backup
nself backup verify bak_20260506_020000
# Output:
# Backup ID: bak_20260506_020000
# Checksum: PASS (SHA-256 matches manifest)
# Encryption: PASS (decrypts successfully)
# Restore test: PASS (restored to ephemeral container)
# Row count: PASS (142,381 rows — matches original)
# Backup is valid.
# Schedule weekly auto-verification
BACKUP_VERIFY_SCHEDULE=0 6 * * 0# Dry run — see what would be deleted
nself backup prune --dry-run
# Output:
# Would delete: bak_20260407_020000 (31 days old)
# Would delete: bak_20260408_020000 (30 days old)
# Would keep: bak_20260409_020000 (29 days old) ... (22 more)
# Apply prune
nself backup prune
# Prune is automatic when BACKUP_RETENTION_DAYS is setnself backup status
# Output:
# Backup agent: running
# Last backup: 2026-05-07 02:00 UTC (12 hours ago)
# Next backup: 2026-05-08 02:00 UTC (in 12 hours)
# Local backups: 22 (of 30 max)
# Remote sync: up to date
# WAL archiving: enabled, lag: 0s
# Storage used: 3.7 GB local, 3.7 GB remotenself backup create --label pre-deploy before every production deploy.BACKUP_ENCRYPT=true and keep the key in your secrets manager, not in the repo.