WIUNIX ACADEMY
Restic offsite backup: prove a specific snapshot restores before trusting it
Page 2 of 3. Review the earlier steps and prerequisites before continuing.
YOUR SYSTEM. YOUR STEPS.
Where are you working?
Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.
USE YOUR OWN VALUES
Configure this tutorial
Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.
Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.
Set values for my environment7 editable values
LET’S BUILD
Initialize the exact new repository once
Only after the destination absence and recovery-key custody checks pass, initialize the encrypted repository. If it already exists, stop and determine ownership; do not replace its password file or reinitialize elsewhere to bypass a connection error. Reuse these two environment lines in later panels; they contain a file reference, not a password value.
Operator terminal; authorized lab context
unset RESTIC_PASSWORD RESTIC_PASSWORD_COMMAND
export RESTIC_REPOSITORY='sftp:backup@backup.example.com:/srv/restic/academy-repo'
export RESTIC_PASSWORD_FILE='/home/operator/academy-restic-private/repository-password'
restic init
restic cat config
restic snapshotsA new repository ID/config is reported and there are no snapshots yet; retain its identity privately.
LET’S BUILD
Record file hashes while the export is stable
Keep writers quiesced or use an immutable native export throughout manifest creation and backup. Hashes validate file bytes only, not ownership, ACLs, extended attributes or application recovery. The manifest stays outside source so it is not accidentally included in the same snapshot. Its file names may be sensitive; keep it private.
Operator terminal; authorized lab context
set -o pipefail
(set -C; : > '/home/operator/academy-restic-private/source-sha256.txt') || exit 1
(cd '/srv/backup-export' && find . -xdev -type f -print0 | sort -z | xargs -0 -r sha256sum --) > '/home/operator/academy-restic-private/source-sha256.txt' || exit 1
test -s '/home/operator/academy-restic-private/source-sha256.txt' || { printf '%s\n' 'No regular files: stop and review source'; exit 1; }A nonempty private file-hash manifest represents the exact stable export selected for backup.
LET’S BUILD
Back up once and retain the exact snapshot ID
This panel writes an encrypted snapshot to the approved remote repository. one-file-system excludes nested mounted filesystems; inventory them first and use separate explicit sources if they are required. Any nonzero exit, including partial-backup status, is a failed acceptance. The JSON summary is private operational output, not public authoring evidence.
Operator terminal; authorized lab context
unset RESTIC_PASSWORD RESTIC_PASSWORD_COMMAND
export RESTIC_REPOSITORY='sftp:backup@backup.example.com:/srv/restic/academy-repo'
export RESTIC_PASSWORD_FILE='/home/operator/academy-restic-private/repository-password'
(set -C; : > '/home/operator/academy-restic-private/backup-result.json') || exit 1
restic backup '/srv/backup-export' --host 'app1' --tag academy-restore --one-file-system --json > '/home/operator/academy-restic-private/backup-result.json'
backup_status=$?
test "$backup_status" -eq 0 || { printf 'Backup failed: %s\n' "$backup_status"; exit 1; }
python3 - <<'PY'
import json
from pathlib import Path
p=Path('/home/operator/academy-restic-private')
rows=[json.loads(line) for line in (p/'backup-result.json').read_text().splitlines() if line.strip()]
summaries=[r for r in rows if r.get('message_type')=='summary']
assert len(summaries)==1 and summaries[0].get('snapshot_id'), 'No unique successful summary'
snapshot=summaries[0]['snapshot_id']
assert len(snapshot)==64 and all(c in '0123456789abcdef' for c in snapshot), 'Invalid snapshot ID'
with (p/'snapshot-id').open('x') as f:f.write(snapshot+'\n')
print('Exact snapshot ID retained in protected file')
PYExit status is0 and one full64-character snapshot ID is retained; the source remained stable.
LET’S BUILD
Separate metadata checking from reading backup data
Read the exact snapshot and perform a metadata check. A normal check does not read/decrypt every data pack. A full read-data check can be expensive and incur remote transfer costs; review repository size/budget and schedule through the existing owner process before choosing it. A subset check covers only its documented subset, not all data.
Operator terminal; authorized lab context
unset RESTIC_PASSWORD RESTIC_PASSWORD_COMMAND
export RESTIC_REPOSITORY='sftp:backup@backup.example.com:/srv/restic/academy-repo'
export RESTIC_PASSWORD_FILE='/home/operator/academy-restic-private/repository-password'
snapshot=$(cat '/home/operator/academy-restic-private/snapshot-id')
restic snapshots "$snapshot"
restic check
# Optional only after capacity/transfer-budget review:
restic check --read-dataThe exact snapshot exists and checks report no errors; record whether full data was actually read.
LET’S BUILD
Restore into a new isolated directory
This restores data locally and may consume substantial space. Re-run the non-overlap/capacity checks if anything changed, and use exclusive mkdir so an existing directory is never a restore target. Absolute snapshot source paths are reproduced below the target: the restored source is restore_dir followed by source_dir. Do not point restore at the live application path.
Operator terminal; authorized lab context
unset RESTIC_PASSWORD RESTIC_PASSWORD_COMMAND
export RESTIC_REPOSITORY='sftp:backup@backup.example.com:/srv/restic/academy-repo'
export RESTIC_PASSWORD_FILE='/home/operator/academy-restic-private/repository-password'
mkdir -m 700 '/srv/academy-restic-restore' || exit 1
snapshot=$(cat '/home/operator/academy-restic-private/snapshot-id')
restic restore "$snapshot" --target '/srv/academy-restic-restore' --verifyThe selected snapshot restores successfully in the new separate target, with no source overwrite.
LET’S BUILD
Compare bytes and then exercise the application export
Verify all manifest-listed regular files under the restored source. This does not detect extra files or verify every metadata attribute; compare file counts and required permissions/ACLs separately. If the source export represents a database, restore it into an isolated database with the native recovery tooling and run application-level integrity checks before claiming recovery. Never attach the rehearsal instance to production queues or outbound delivery.
Operator terminal; authorized lab context
cd '/srv/academy-restic-restore/srv/backup-export' || exit 1
sha256sum --check '/home/operator/academy-restic-private/source-sha256.txt' || exit 1
find . -xdev -type f -printf '.' | wc -c
find '/srv/backup-export' -xdev -type f -printf '.' | wc -cAll selected hashes match and the reviewed file inventory/metadata and application restore checks meet the recovery contract.