OneUptime backup and restore¶
OneUptime is tier 1 because it holds canonical incident, acknowledgement, timeline, workflow, and audit state.
Protected state¶
| State | Protection |
|---|---|
| PostgreSQL | Custom-format pg_dump |
| ClickHouse | Built-in BACKUP DATABASE to a dedicated named volume |
| OneUptime secrets | SOPS-encrypted .env.sops.yaml in Git |
| Exact runtime | Digest-pinned Compose in Git |
| Redis | Not backed up; cache and queues rebuild empty |
ENCRYPTION_SECRET is required with the PostgreSQL restore. A database backup
without the matching encrypted SOPS file is not a complete recovery set.
Create a logical backup¶
Run as root on infra-services:
The script:
- writes a PostgreSQL custom-format dump to
backups/postgres/oneuptime-<timestamp>.dump; - verifies the dump catalog;
- runs ClickHouse
BACKUP DATABASE oneuptimetooneuptime_clickhouse-backups; - writes a SHA-256 file and a manifest linking both artifacts.
The restic job runs this script as its pre-hook, then backs up the PostgreSQL staging directory and ClickHouse backup volume to the local repository and B2.
The script does not delete old local logical backups. Deleting or relocating backup artifacts requires explicit owner approval. Monitor staging growth and make an owner-approved pruning decision after restic/offsite verification.
Isolated restore drill¶
Run after a successful logical backup:
The drill:
- verifies the PostgreSQL dump checksum;
- restores PostgreSQL into a temporary container and volume;
- mounts ClickHouse backup artifacts read-only into a temporary ClickHouse container and volume;
- restores the database under
oneuptime_restore; - requires non-zero table counts in both datastores;
- removes only temporary test containers and volumes.
It does not modify production databases or delete backup artifacts.
Restore from restic¶
export RESTIC_REPOSITORY=/mnt/infra-backups/homelab-restic/oneuptime
export RESTIC_PASSWORD_FILE=/etc/restic/password
restic snapshots
restic restore latest --target /tmp/restore-oneuptime
For offsite recovery:
source /etc/restic/b2.env
export RESTIC_REPOSITORY=b2:<bucket>:oneuptime
export RESTIC_PASSWORD_FILE=/etc/restic/password
restic restore latest --target /tmp/restore-oneuptime
Production recovery¶
- Stop ingress and app. Leave Redis disposable.
- Start empty PostgreSQL and ClickHouse containers at the exact backed-up versions.
- Restore the SOPS file and render
compose.env; verify the originalENCRYPTION_SECRETis present. - Restore PostgreSQL:
docker exec -i oneuptime-postgres \
pg_restore --username oneuptime --dbname oneuptime \
--clean --if-exists \
</tmp/restore-oneuptime/opt/homelab/services/oneuptime/backups/postgres/<dump>
- Copy the restored ClickHouse backup set into the
oneuptime_clickhouse-backupsvolume without deleting other sets. - Restore ClickHouse:
docker exec oneuptime-clickhouse bash -c '
clickhouse-client \
--user "$CLICKHOUSE_USER" \
--password "$CLICKHOUSE_PASSWORD" \
--query "RESTORE DATABASE oneuptime FROM Disk('\''backups'\'', '\''<backup-name>'\'') SETTINGS async = false"
'
- Start the app with workflow/remediation integrations disabled.
- Verify users, projects, incidents, timelines, workflow runs, and encrypted provider values.
- Start ingress and run
scripts/smoke-test.sh --live. - Re-enable notification and remediation workflows only after review.
If restoring over an existing ClickHouse database, use an isolated temporary
database first. Never issue DROP DATABASE or remove a backup volume without
explicit owner approval.
Acceptance¶
- PostgreSQL and ClickHouse backups share the same manifest timestamp.
- The isolated drill reports non-zero table counts.
- Restic has a local snapshot and tier-1 B2 copy.
- The matching SOPS file decrypts on the recovery host.
- The exact OneUptime images remain available.
- Application readiness and incident history pass after restore.