mirror of
https://github.com/tildeclub/tilde.club.git
synced 2026-10-25 00:55:38 +00:00
170 lines
7.9 KiB
Markdown
170 lines
7.9 KiB
Markdown
# Backup and recovery
|
|
|
|
The collected host runs Borg 1.4.5. Root's crontab invokes
|
|
`/usr/bin/borgmatic` at 03:00 daily; that executable reports 2.1.9. A separate
|
|
`/usr/local/bin/borgmatic` reports 1.5.21 and appears earlier in the collector's
|
|
PATH. Use the absolute path from the actual job when checking versions,
|
|
validating configs, or reproducing a run.
|
|
|
|
The production backup configuration and credentials were not collected. The
|
|
example here is a new-host plan, not a claim about its repository, retention,
|
|
or current source coverage. Historical backup logs are also kept on the
|
|
operator's NAS; missing local journal history is not proof a backup did not run.
|
|
|
|
## Decide what must be recoverable
|
|
|
|
Include these in the plan:
|
|
|
|
| Data | What to capture |
|
|
| --- | --- |
|
|
| Accounts | `/etc`, homes, UIDs/GIDs, keys, shell defaults, quotas, and group memberships. |
|
|
| Member publishing and mail | Public directories, private mailbox data, forwarding and Sieve files. |
|
|
| Local code | `/usr/local`, source revisions, application lockfiles, service definitions, and build notes. |
|
|
| Operator web apps | Code, uploaded content, config, and a consistent database backup. |
|
|
| Service state | ZNC, INN spool/history, Gemini/Gopher content, and other app-specific state. |
|
|
| Secrets | Restricted encrypted copies of signing keys, service credentials, and recovery material. |
|
|
| Backup recovery | Repository key, passphrase, SSH credentials, destination, and restore instructions held off the host. |
|
|
|
|
Do not assume all state lives in `/home`. Inspect service units and application
|
|
configs. A Postfix queue restore needs its own policy because re-delivering
|
|
queued mail can duplicate messages. Reinstalling a database package and copying
|
|
its live data directory is not a substitute for a supported database restore.
|
|
|
|
## Prepare a remote repository
|
|
|
|
Install the maintained `borgbackup` and `borgmatic` packages. Configure a
|
|
dedicated backup account/key on the destination and verify the host key through
|
|
a trusted channel. Restrict the remote key to the intended repository and
|
|
operations using the destination's supported Borg setup.
|
|
|
|
Keep the repository passphrase in a root-only file outside the repo. Create
|
|
`/root/.config/borg` at 0700 and the passphrase file at 0600 using a root editor
|
|
or password-manager procedure; do not put the passphrase in a shell command,
|
|
git commit, or public config archive.
|
|
|
|
For the Borg **1.x** example, initialize a new repository from a root Bash shell:
|
|
|
|
```bash
|
|
export BORG_PASSCOMMAND='cat /root/.config/borg/passphrase'
|
|
repository='ssh://backup@backup.example.net/./tilde'
|
|
/usr/bin/borg init --encryption=repokey-blake2 "$repository"
|
|
/usr/bin/borg key export "$repository" /root/tilde-borg-key-export
|
|
```
|
|
|
|
Store the export and the passphrase off the source host through a protected
|
|
channel. Test access from a recovery machine. Do not initialize a repository
|
|
that is already in use, and do not substitute Borg 2 commands or formats into a
|
|
Borg 1 backup without planning that migration.
|
|
|
|
## Configure borgmatic
|
|
|
|
Review [config.yaml](../examples/borgmatic/config.yaml). Put the adjusted file
|
|
in `/etc/borgmatic/config.yaml` with root ownership and mode 0600. It uses the
|
|
flat borgmatic 2.x schema, not the old 1.5 `location/storage/retention` sections.
|
|
Create the database-dump directory at mode 0700 before validation:
|
|
|
|
```bash
|
|
install -d -o root -g root -m 0700 /var/backups/tilde-databases
|
|
/usr/bin/borgmatic config validate
|
|
```
|
|
|
|
Add existing service-state directories to `source_directories`. Keep the
|
|
missing-source check enabled so a typo or missing mount fails visibly. If a
|
|
required data mount can disappear while leaving an empty mountpoint behind,
|
|
add a mount-identity check too: merely testing that a directory exists is not
|
|
sufficient.
|
|
|
|
The example keeps daily, weekly, and monthly restore points. Choose retention
|
|
based on available storage and the longest period a bad change might go
|
|
unnoticed. A live filesystem backup is not a simultaneous snapshot of every
|
|
file. Quiesce services or use consistent snapshots where that matters.
|
|
|
|
## Consistent databases
|
|
|
|
Use borgmatic's supported database hooks for your installed version, or prepare
|
|
logical backups before running it. Here is a small wrapper for a host with a
|
|
local MariaDB instance. Save it as `/usr/local/sbin/backup-tilde`, root-owned and
|
|
mode 0700, and adjust the database section if MariaDB is not part of your host:
|
|
|
|
```bash
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
export PATH=/usr/sbin:/usr/bin:/sbin:/bin
|
|
umask 077
|
|
|
|
dump_dir=/var/backups/tilde-databases
|
|
install -d -m 0700 "$dump_dir"
|
|
dump_file=$(mktemp "$dump_dir/.mariadb.XXXXXXXX.sql")
|
|
trap 'rm -f -- "$dump_file"' EXIT
|
|
|
|
mariadb-dump --all-databases --single-transaction --routines --events \
|
|
--triggers --hex-blob > "$dump_file"
|
|
mv -f -- "$dump_file" "$dump_dir/mariadb.sql"
|
|
|
|
/usr/bin/borgmatic create prune compact check --verbosity 1
|
|
```
|
|
|
|
This assumes a local root socket login or a separately protected client config;
|
|
it does not embed a database password. `--single-transaction` gives a consistent
|
|
view of transactional tables, not arbitrary nontransactional tables or concurrent
|
|
schema changes. Check engines and use a suitable lock, snapshot, or maintenance
|
|
window where needed. A failed dump must stop the backup rather than silently
|
|
reuse yesterday's SQL.
|
|
|
|
For SQLite, use its backup API or the CLI `.backup` operation while the app is
|
|
running. Do not copy a database file while ignoring an active WAL. For other
|
|
databases, use their supported consistent export/snapshot method.
|
|
|
|
## Schedule and monitor
|
|
|
|
Run the wrapper manually once and inspect the resulting archive. For a
|
|
cron-based setup, a root crontab entry can be:
|
|
|
|
```cron
|
|
0 3 * * * /usr/bin/flock -n /run/tilde-backup.lock /usr/local/sbin/backup-tilde >> /var/log/tilde-backup.log 2>&1
|
|
```
|
|
|
|
Create the log at mode 0600 and configure root-only log rotation. This example
|
|
runs at 03:00 in cron's configured timezone; record that timezone. Do not enable
|
|
a second timer for the same job without intending two schedules. A skipped
|
|
lock acquisition is not a successful backup and needs to be visible in monitoring.
|
|
|
|
Alert when there is no successful archive within the expected interval, not
|
|
only when the scheduler launches a command. Check off-host capacity, archive
|
|
age, consistency checks, repository access, database exports, and restore tests.
|
|
Store monitoring/notification credentials separately from the public docs.
|
|
|
|
## Restore an account in isolation
|
|
|
|
On a recovery machine or isolated directory, use the matching Borg version and
|
|
repository key. With `BORG_PASSCOMMAND` set as above:
|
|
|
|
```bash
|
|
repository='ssh://backup@backup.example.net/./tilde'
|
|
/usr/bin/borg list "$repository"
|
|
install -d -m 0700 /root/tilde-restore-test
|
|
cd /root/tilde-restore-test
|
|
/usr/bin/borg extract --dry-run "$repository::ARCHIVE" home/member
|
|
/usr/bin/borg extract "$repository::ARCHIVE" home/member
|
|
```
|
|
|
|
Replace `ARCHIVE` with the exact archive name returned by the list command.
|
|
Check the stored path names first. Extract relative paths into the test
|
|
directory; do not start a restore by extracting over `/home` or `/etc`.
|
|
|
|
Verify file contents, private modes, ownership, symlinks, mail, public pages,
|
|
and database exports. Preserve numeric UIDs/GIDs or deliberately map them before
|
|
returning restored data to service. Reapply SELinux labels and re-create quota
|
|
limits on the restored filesystem. Test login on an isolated host, not by
|
|
announcing a restored production account before it has been checked.
|
|
|
|
For a whole-host restore, reinstall a supported OS, restore account identity
|
|
and data deliberately, restore databases using their matching tools, restore
|
|
service secrets, then enable services in dependency order. Keep outbound mail,
|
|
IRC bots, signup announcements, and scheduled jobs disabled until duplicate
|
|
external actions have been ruled out. Change DNS or reopen public access only
|
|
after the replacement has passed its service checks.
|
|
|
|
Record the last successful restore date, what was restored, and what failed.
|
|
A recovery key kept only inside the encrypted backup cannot recover that backup.
|