Off-site Backup Replication
Heratio can copy your local backup archives to an off-site destination on a daily schedule. This protects your data against site loss (fire, hardware failure, theft, ransomware on the local box). It is independent of the in-app Backup dashboard - that dashboard creates the local archives, this feature ships them off the host.
What gets replicated
Every file in your backup directory (the path shown on /admin/backup -> Settings, defaulting to storage/backups) that matches *.gz, *.tar.gz, *.sql.gz, or *.zip. Already-replicated files are skipped automatically so re-running the command is cheap.
Choosing a destination
You pick one of three drivers via the BACKUP_OFFSITE_DRIVER environment variable:
- s3 - any S3-compatible object store (AWS S3, Wasabi, Backblaze B2, MinIO, DigitalOcean Spaces). This is the recommended choice for most installs.
- rsync - copy over SSH to another server you control. Use this when you already have a backup host and prefer not to put data in a third-party object store.
- localfs - copy to another directory on the same host. Useful for testing the pipeline before pointing at real off-site storage. Provides ZERO disaster-recovery value on its own.
See docs/reference/backup-phase-3-offsite-integrity.md for the full environment-variable reference for each driver.
Encryption
If you set the AHG setting backup_encryption_passphrase (group: backup), Heratio will GPG-encrypt every archive with AES256 before pushing it. Keep the passphrase somewhere safe (and OFF the Heratio host) - if you lose it, the off-site copies become unrecoverable. Without a passphrase the off-site copy is shipped unencrypted and the daily command emits a warning.
The local archive in storage/backups/ is never encrypted, so restoring from the local copy still works through the Backup dashboard as usual.
Running it
By hand:
php artisan backup:replicate
The scheduler automatically runs it daily at 03:15 if you have Laravel's scheduler wired into cron (* * * * * cd /usr/share/nginx/heratio && php artisan schedule:run).
Verifying integrity
Off-site copies are useless if they have silently corrupted. Heratio re-verifies the SHA-256 of every replicated file on a daily schedule (04:00). To run it on demand:
php artisan backup:verify-integrity
Add --all to re-check files already marked verified (defence in depth). Add --from=2026-05-01 to limit the sweep to recent uploads.
Any file that fails verification appears in the application log (storage/logs/laravel-<date>.log) and is marked status='failed' in the ahg_backup_replication table. Operators are expected to react: re-replicate the file with php artisan backup:replicate --force, then verify again.
Where to see history
The ledger lives in the ahg_backup_replication table. A future Backup dashboard tab will surface this in the UI; for now, query it directly:
SELECT local_path, driver, status, replicated_at, verified_at
FROM ahg_backup_replication
ORDER BY replicated_at DESC
LIMIT 50;
Troubleshooting
- "S3OffsiteDriver requires the aws/aws-sdk-php package" - run
composer require aws/aws-sdk-phpin the Heratio root. - "the
gpgbinary is not on PATH" - install gnupg (apt install gnupg) or clearbackup_encryption_passphraseif you do not want encryption. - rsync exits non-zero with
Permission denied (publickey)- checkBACKUP_OFFSITE_RSYNC_SSH_KEYpoints at the right private key and the matching public key is in the remote user's~/.ssh/authorized_keys. - All pushes go to
status='failed'with a SHA-256 mismatch - check that nothing else (a sync tool, the cloud provider's own dedup) is rewriting the object after upload.
Not in this release
- Point-in-time recovery (PITR) from binary logs.
- Granular restore (single table, single file).
- A formal Disaster Recovery plan document.
Those three items are tracked under issue #671 Phase 4.