Backup, Restore, and Recovery
This guide applies to the supported single-host production Compose deployment. The repository ships guarded commands for local backups, restoration, scheduling, encrypted off-site copies, and freshness monitoring.
What must be protected
| Data | Location | Impact if lost |
|---|---|---|
| Forum database | neo4j-data Docker volume | Forum content and configuration are lost |
| Auth0 sessions | frontend-data Docker volume | Users are signed out |
| Production secrets | .env.production | Services cannot be recovered with the same identities and encryption keys |
| Uploaded objects | Configured GCS buckets | Images and downloadable files are lost |
The backup command protects the two Docker volumes. It deliberately excludes
.env.production; store that file and its secrets separately in encrypted
secret storage. Protect object-storage data with bucket retention, versioning,
or a separate provider backup policy.
Create a cold backup
Create a protected destination and run the repository command from the deployment checkout:
sudo install -d -m 0700 -o "$(id -un)" -g "$(id -gn)" \
/var/backups/multiforum
scripts/backup-self-hosting.sh \
--env-file .env.production \
--output-dir /var/backups/multiforum
The command:
- validates the production Compose model and expected volumes;
- requires the database, backend, and frontend to be running;
- stops the write path and database;
- archives Neo4j and frontend sessions;
- writes a manifest containing selected image versions and SHA-256 checksums;
- restarts the services even if archiving fails; and
- publishes the completed bundle atomically.
The site has a brief outage while the volumes are copied. Each completed directory contains:
manifest.json
neo4j-data.tar.gz
frontend-data.tar.gz
Optional count-based local retention runs only after a new backup completes and the services restart:
scripts/backup-self-hosting.sh \
--env-file .env.production \
--output-dir /var/backups/multiforum \
--retention-count 7
Only complete directories with the exact generated timestamp format are eligible for pruning. Failed or incomplete backups do not trigger retention.
Schedule daily backups
The AWS Terraform example stages, but does not enable, a systemd service and
timer. First complete a successful manual backup. Then review
/etc/multiforum/backup.env and enable the schedule:
sudo systemctl start multiforum-backup.service
sudo systemctl enable --now multiforum-backup.timer
systemctl list-timers multiforum-backup.timer
The timer is persistent and adds a randomized delay of up to 30 minutes. Review recent runs with:
systemctl status multiforum-backup.service
journalctl -u multiforum-backup.service --since "2 days ago"
A scheduled local copy is not an off-site backup.
Encrypt and upload with Restic
The Terraform host includes Restic and stages an inactive systemd drop-in. Copy and restrict the example configuration:
sudo install -m 0600 \
/etc/multiforum/restic.env.example \
/etc/multiforum/restic.env
sudo install -m 0600 /dev/null /etc/multiforum/restic-password
sudoedit /etc/multiforum/restic.env
sudoedit /etc/multiforum/restic-password
Set RESTIC_REPOSITORY, add credentials required by the selected Restic
backend, and choose a tag unique to the instance. Store the Restic password
somewhere separate from both the server and repository.
Initialize the empty repository once:
sudo bash -c '
set -a
source /etc/multiforum/restic.env
set +a
restic init
'
Test an upload manually:
sudo bash -c '
set -a
source /etc/multiforum/restic.env
set +a
/opt/multiforum/scripts/upload-self-hosting-backup.sh \
--backup-root /var/backups/multiforum \
--keep-daily "$MULTIFORUM_BACKUP_RESTIC_KEEP_DAILY" \
--tag "$MULTIFORUM_BACKUP_RESTIC_TAG"
'
The command verifies the newest bundle's manifest and checksums before remote access. It uploads the bundle encrypted, then applies tag-scoped remote retention. A failed upload never runs retention.
After a successful test, activate the staged drop-in:
sudo install -m 0644 \
/etc/systemd/system/multiforum-backup.service.d/offsite.conf.example \
/etc/systemd/system/multiforum-backup.service.d/offsite.conf
sudo systemctl daemon-reload
sudo systemctl start multiforum-backup.service
The off-site upload runs only after the local backup succeeds. A remote failure marks the service failed without deleting the new local bundle.
Monitor freshness and integrity
Check a local bundle:
scripts/check-self-hosting-backups.sh \
--backup-root /var/backups/multiforum \
--max-age-hours 36
Require recent local and off-site protection and emit compact JSON:
sudo bash -c '
set -a
source /etc/multiforum/restic.env
set +a
/opt/multiforum/scripts/check-self-hosting-backups.sh \
--backup-root /var/backups/multiforum \
--max-age-hours 36 \
--restic-tag "$MULTIFORUM_BACKUP_RESTIC_TAG" \
--offsite-max-age-hours 48 \
--json
'
The check exits nonzero when local or remote protection is missing, stale, corrupt, future-dated, or unreachable. Send its result to a monitoring agent and alert when the check does not run. Also use an external uptime check, since the Multiforum host cannot report its own outage.
Restore a cold backup
Restore on a separate host first whenever possible. Preserve the original environment file and create a fresh safety backup before replacing current data. Inspect the bundle's recorded images:
jq .images /var/backups/multiforum/BACKUP_DIR/manifest.json
Configure the recorded Neo4j image. Neo4j stores are not portable across arbitrary database versions. Stop the application and database:
docker compose \
--env-file .env.production \
-f docker-compose.yml \
-f docker-compose.production.yml \
stop frontend backend
docker compose \
--env-file .env.production \
-f docker-compose.yml \
-f docker-compose.production.yml \
stop database
Run the guarded restore with explicit destructive confirmation:
scripts/restore-self-hosting.sh \
--backup-dir /var/backups/multiforum/BACKUP_DIR \
--env-file .env.production \
--confirm-replace-existing-data
The command verifies the manifest, checksums, archive paths, stopped-service precondition, and configured Neo4j image before replacing volumes. It never starts application services. After success, validate, start, and verify:
docker compose \
--env-file .env.production \
-f docker-compose.yml \
-f docker-compose.production.yml \
config --quiet
docker compose \
--env-file .env.production \
-f docker-compose.yml \
-f docker-compose.production.yml \
up -d
scripts/verify-self-hosting.sh --env-file .env.production
Use --allow-database-image-mismatch only after reviewing Neo4j compatibility.
Restoring the original Auth0 session secret preserves the ability to decrypt
restored sessions; otherwise users may need to sign in again.
Restore from Restic
On a recovery host with the Restic password and provider credentials:
sudo install -d -m 0700 /var/tmp/multiforum-restic-restore
sudo bash -c '
set -a
source /etc/multiforum/restic.env
set +a
restic restore latest \
--tag "$MULTIFORUM_BACKUP_RESTIC_TAG" \
--target /var/tmp/multiforum-restic-restore
'
sudo find /var/tmp/multiforum-restic-restore -name manifest.json -print
Locate the bundle containing the manifest and use it with the guarded restore command. Securely remove the temporary restore tree afterward.
Recovery drills
At a regular interval:
- Restore the newest remote snapshot onto a separate host.
- Start Multiforum with the recorded image versions.
- Run production verification.
- Complete an Auth0 login and representative read/write operations.
- Record recovery time and any manual steps.
- Run
restic checkand investigate failures immediately.
A backup that has never been restored is not yet a proven recovery plan.