RCWRCW IT TrainingFree hands-on labs & simulators← Back to home
Backup · Troubleshooting guide

rsync and restic Failures: Exit Codes, Locks and the Restore You Never Tested

Backup tools fail in two ways: loudly (a failed run) and silently (a run that "succeeded" while skipping what mattered). This guide covers the loud ones by exit code, then the discipline that catches the silent ones.

Published August 29, 2026 · RCW IT Training

rsync exit codes that matter

CodeMeaning and response
23 — partial transferSome files could not be sent: permission denied, vanished mid-run, or a full destination. The per-file lines above the exit code name them; --ignore-errors is a confession, not a fix
11 — file I/O errorRead errors on source (dying disk!) or write errors on target; check dmesg on both ends before rerunning
255 / code 12 — connectionssh broke mid-transfer: network blip or sshd timeout; for long runs add --timeout/--contimeout and ServerAliveInterval on the ssh side
24 — vanished filesSource files deleted during the run; expected on live systems, noise unless constant

Exit 23 with Permission denied lines: run as root, or accept it consciously with --super/--no-perms semantics understood — silently skipping unreadable files is how backups lose exactly the sensitive directories.

The trailing-slash bug and other "wrong data" failures

rsync -a /data backup:/bk/ copies the directory; rsync -a /data/ backup:/bk/ copies its contents. The wrong variant produces a backup that restores to the wrong shape — a success that fails at restore. Standardise one form in your scripts and document it. Related traps: forgetting -a (no recursion/permissions), and --delete pointed at the wrong target — test a new rsync line once with -n before scheduling it.

restic: locks, integrity and repository errors

restic -r /repo snapshots            # is the repo readable at all?
restic -r /repo check --read-data-subset=5%   # real integrity sampling
restic unlock                        # ONLY after confirming no backup is running
  • "repository is locked": a previous run died (OOM, Ctrl-C, host crash) leaving a stale lock. Confirm nothing is running on any host sharing the repo, then restic unlock. Never unlock blindly on a shared repo.
  • OOM during backup of huge trees: restic scans and packs in memory; limit with --pack-size considerations or exclude monster directories; the process dying silently mid-run is the classic "no snapshot since Tuesday".
  • check failures: run restic repair index first; data-loss-level corruption means restore what verifies from older snapshots — another argument for check in the schedule, not just backup.

Scheduling failures: the backup that never ran

The most common backup outage is a cron that stopped: a changed passwordless-ssh key, a moved repo mount, or the job waiting forever on a hung NFS source. Detect it from the destination side — a freshness check beats reading cron mail:

# on the backup target or a monitor:
find /backups/host1 -maxdepth 1 -mtime -2 | grep . || echo "NO FRESH BACKUP"

Alert on snapshot age per host, and on the job's own runtime doubling (a backup that suddenly takes 10× is reading a broken disk or lost its incremental state).

Restore verification: the habit that makes all of this real

  • Weekly: restore a random snapshot to scratch space and diff -r a sample tree; log the result.
  • Quarterly: full restore drill of one critical dataset, timed, with the runbook in hand.
  • For restic: restic restore --target /drill <snap> --include /critical/path; time it — your RTO is the measured number, not the hoped one.
  • After every tool or OS upgrade on the backup host, run one drill before trusting the next scheduled run.

Keys: the part of encrypted backups everyone forgets

A restic repository without its key is noise; a key without discipline is a single-file loss of every backup. Keep at least one recovery copy of the key offline and separate from the repository (a different vault, a sealed-envelope process, a secrets manager with its own access review), and prove the copy works: a quarterly drill that restores using the recovery key, not the one cached on the backup host. restic key list and restic key add let several keys protect one repo — issue a dedicated key to the drill so it never touches production credentials. And keep the threat model split in view: object lock makes backups undeletable, encryption makes them unreadable — you need both properties, and "stolen bucket plus leaked key" is the scenario that tests whether key storage was real.

The four numbers to alert on

MetricWhy
Snapshot/run age per hostdetects the cron that died
Run duration vs baselinea 10× run reads a dying disk or lost incremental state
Exit code != 0 (and != 24 where expected)loud failures, caught at run time
Repo/bucket canary listingcredential and endpoint drift, day one
Key takeaway: Read rsync failures by exit code (23 permissions/vanished, 11 I/O and dying disks, 255 transport), treat restic locks as evidence of dead runs not bugs, monitor backup freshness from the destination, and schedule restore drills — an untested backup is a rumour.