Australian cloud for business software — built and run in Sydney since 2003

NetLeverage
NetLeverage

What restic and Borg repository checks prove, and what only a restore can

Technical article
Backup & Recovery

About this article: AI-assisted writing, reviewed against the cited sources. This is source research, not lab validation. Check the stated versions and your own requirements before making changes.

restic and Borg are open-source backup tools often run on Linux servers. Both use deduplicated repositories and offer integrity checks. restic encrypts repository data; Borg also supports encrypted repositories, and its tamper-resistance claims must be read with the configured mode and key protections in mind.

A passing check is worth having. It is also easy to read more into it than it says. Each tool has a quick check and a thorough one, and even the thorough one answers a specific question: is what is stored intact and consistent? Whether what is stored is what you needed, and whether another operator can recover access after losing the normal backup environment, are separate questions.

This guide sets out what each level of check covers according to the projects' own documentation, what each costs to run, and how to add a restore test that covers the rest.

Why it matters

A backup repository can fail in several distinct ways (our reading):

  1. The repository's structure is damaged. An index, snapshot or tree file is missing or corrupt.
  2. The stored data has changed. Disk faults, a storage provider fault or deliberate tampering alter pack or segment files.
  3. The data was bad when it was stored. Memory faults or a damaged source file were captured faithfully.
  4. The data was never stored. An exclusion, a wrong path or a job that silently stopped means the file is not in any snapshot.
  5. The repository cannot be opened. The password, passphrase or key file is lost.

Repository checks can detect the structural and stored-integrity problems they cover. They do not establish that originally captured content was meaningful or that every required file was included. A check that needs decryption also exercises the credentials available to that process and can fail when they are missing or wrong. A successful run still does not prove that a second operator can retrieve the keys after the backup host is lost; that needs a separate recovery test.

restic: two levels of check

The default check

The restic manual describes "two types of checks": "Structural consistency and integrity, e.g. snapshots, trees and pack files (default)" and "Integrity of the actual data that you backed up (enabled with flags, see below)".

Run without options, restic check verifies the structure. The manual's example shows the result for a damaged index: check "will detect this and yield the same error as when you tried to restore". When the structure is intact, it reports "no errors were found".

What the default check does not do is read your backed-up data. The manual is explicit: "By default, the check command does not verify that the actual pack files on disk in the repository are unmodified, because doing so requires reading a copy of every pack file in the repository."

Reading the data

restic check --read-data adds that step. The manual notes the cost: "Since --read-data has to download all pack files in the repository, beware that it might incur higher bandwidth costs than usual and also that it takes more time than the default check." For a repository in cloud object storage, that can mean paying egress for the whole repository on every full check (our reading).

--read-data-subset spreads the work. It accepts three forms:

  • n/t checks one part out of t. Running 1/5 through 5/5 on successive occasions covers every pack file once.
  • A percentage, such as 10%, checks a randomly chosen share. The manual warns that this "will not guarantee to cover all available pack files after sufficient runs".
  • A size, such as 50M or 10G, is "converted to percentage of the total repository size" and then behaves like the percentage form.

Cycling all n/t parts provides deliberate coverage rather than random sampling. Record successful completion and account for repository changes during the cycle; a schedule alone does not prove which data has been checked. Random subsets do not guarantee complete coverage.

When check finds a problem

The manual's warning is direct: "If check reports an error in the repository, then you must repair the repository. As long as a repository is damaged, restoring some files or directories will fail. New snapshots are not guaranteed to be restorable either." Repair is a separate, changing operation; follow the troubleshooting section and the instructions check prints, ideally after copying the repository (our recommendation).

Check is not entirely passive

restic's design reference says restic processes "are required to create a lock on the repository before doing anything." Locks are files in the repository's locks directory. The check command also, by default, "creates a new temporary cache directory". So a check does not change your backup data, but it does write a lock file to the repository and temporary files locally (our reading). If the repository is on storage with write restrictions, confirm the check works there before relying on it.

Borg: repository, archives and data

What borg check covers

The Borg manual says "The check command verifies the consistency of a repository and its archives" in "two major steps":

  • The repository check covers "the segment magic headers, and both the metadata and data of all objects in the segments. The read data is checked by size and CRC. Bit rot and other types of accidental damage can be detected this way." For a remote repository, "the checks run on the server and do not cause significant network traffic."
  • The archive check ensures "that the repository manifest exists, the archive metadata chunk is present, and that all chunks referencing files (items) in the archive exist." This "requires reading archive and file metadata, but not data." Because it needs to decrypt, "archive checks run on the client machine".

--repository-only and --archives-only run either step alone.

Full cryptographic verification

--verify-data goes further. Borg describes it as "a full integrity verification (as opposed to checking the CRC32 of the segment) of data, which means reading the data from the repository, decrypting and decompressing it." It is "very time consuming, but will detect any accidental and malicious corruption."

That last phrase carries a condition in the next sentence: "Tamper-resistance is only guaranteed for encrypted repositories against attackers without access to the keys." The FAQ makes the same distinction. For accidental damage, check "will notice corruption using CRCs and hashes"; to detect "malicious tampering also, use an encrypted repo."

Partial checks

--max-duration splits a long repository check into pieces, and "The next partial check will continue where the previous one stopped". The manual's example is a seven-hour check run one hour a day, giving "one full repository check per week."

The limits matter. Partial checks require --repository-only, and the manual warns they "can only perform non-cryptographic checksum checks on the segment files". A full check "can also do a repository index check", and archive checks are excluded from partial runs. Also, "Doing a full repository check aborts any previous partial check; the next partial check will restart from the beginning."

Repair mode

Borg states that "The check command is a read-only task by default." Adding --repair changes that, and the manual is blunt: "--repair is a POTENTIALLY DANGEROUS FEATURE and might lead to data loss!" It recommends "to create a backup of your repository before running in repair mode". The FAQ adds: "We recommend to first run without --repair to assess the situation."

What no check can tell you

Data that was already bad

Borg's FAQ describes a case that applies to deduplicating storage generally. If data is corrupted in memory after its identifier is computed, the repository holds bad data under a correct identifier. A --verify-data run will flag it, because it compares the data with its identifier again. But the FAQ also says checking "cannot detect if the data was already "garbage" when it was first processed and stored." The FAQ recommends reliable hardware: "ECC RAM is recommended."

Data that was never stored

A check verifies what is in the repository. A file excluded by a pattern, a mount that was not crossed, or a path nobody added is simply absent, and every check will pass (our reading). The only test for that is to look for specific files in specific snapshots, which is what a restore test does.

Keys and passwords

restic's manual repeats one line throughout its repository set-up guide: "Losing your password means that your data is irrecoverably lost." Borg says that its "repokey keys are stored inside the repository", keyfile keys are stored locally, and "In any case, you MUST make sure to have an independent backup of the borg keys". A check run on the backup server, with the key in its usual place, proves nothing about whether a second person could open the repository after that server is gone (our reading).

A layered checking routine

A pattern that matches these documents (our recommendation; adjust intervals to your repository size, storage costs and recovery needs):

Layer restic Borg What it proves
Routine repository checks restic check Full borg check --repository-only, or explicitly recorded partial runs restic checks structural consistency; a full Borg repository check covers its segment/index scope. A partial Borg run checks only the visited segments and does not establish archive or full index consistency.
Recorded coverage cycle Successful restic check --read-data-subset=n/t runs across every part Completed full borg check Record successful runs, times and scope. Failed, missed or incomplete runs do not establish cycle completion; archive and data-verification scope remains different.
Periodic deep check restic check --read-data borg check --verify-data All stored data matches its identifiers
Restore test Restore named files and compare Extract named files and compare The files you need are present and readable
Key test Open the repository from a separate machine with the recorded password Same, with the exported key and passphrase Someone else can unlock it

Before scheduling the deep check against cloud storage, estimate the download volume and cost (our recommendation).

Commands for checking

The examples are for a Bash shell on an isolated Linux test system. Substitute only an authorised test repository and test credentials supplied through the documented method. The paths below are placeholders, not instructions to access an existing production repository. The check commands below do not alter backup data, but restic writes a lock file as described above. Do not add --repair or run restic repair while assessing.

# restic, default structural check
restic -r /srv/restic-repo check

# restic, read one fifth of the pack files
restic -r /srv/restic-repo check --read-data-subset=1/5

# Borg, repository and archive check
borg check /path/to/repo

# Borg, full cryptographic data check
borg check --verify-data /path/to/repo

Keep the full output. A summary line saying no errors were found is evidence; a scheduled job whose output nobody reads is not (our reading).

A restore test that covers the gaps

Checks prove the repository. A restore test proves the recovery. Do it in an authorised, isolated test environment, not on the server being protected (our recommendation).

  1. Prepare synthetic data. On a dedicated isolated test server that mirrors the relevant layout, create a small set of known files: text, a binary, a file with spaces in its name, and a file in each directory your job is meant to cover. Record a SHA-256 hash of each with sha256sum.
  2. Back them up to a dedicated test repository, using a reviewed copy of the relevant scope, exclusions and options. Replace all production destinations, credentials and external hooks with authorised test equivalents. Do not run the real production job or touch customer data. Synthetic tests establish coverage only for the paths and settings actually exercised.
  3. On a separate test machine, using a password or key retrieved from your recorded key store rather than the server, open the repository.
  4. Preview the restore. restic's restore --dry-run --verbose=2 prints what would be restored; the manual notes that a dry run checks existing files at the target, and that you can avoid that by specifying "a non-existing --target directory". Borg's extract --dry-run performs "all extraction steps except actually writing the output data".
  5. Restore into an empty, new directory on the test machine. restic's restore "overwrites already existing files at the target directory" by default, so never point it at live data. Borg extracts into the current directory, so change to an empty one first (our understanding).
  6. Compare hashes with those recorded in step 1.
  7. Diagnose by symptom. A file absent from the snapshot points to job scope (excludes, paths, mounts). A file present but with a different hash needs comparison with the intended backup-time version and source baseline. A deep integrity check can assess stored-data consistency, but cannot establish that the chosen snapshot or originally captured content is the version the business intended. A repository that will not open needs diagnosis of credentials, permissions, storage reachability and repository condition; the error alone does not prove a lost key.

Have a qualified person review the procedure before adapting it to production data.

Worked example (illustrative)

The following is invented to show the pattern. A consultancy backs up its Linux file server to a restic repository in cloud object storage. A nightly restic check has passed for two years. When a client asks for an old proposal, the file is not in any snapshot: a project folder had been moved onto a new disk mount, and the job used --one-file-system. Every check had been accurate, because the repository was consistent. It simply did not contain the folder.

The consultancy adds a monthly restore test of one named file from each top-level share, a rolling --read-data-subset check, and a quarterly test of the repository password from a laptop that is not on the office network.

Risks and limits

  • This guide reflects restic 0.19.1 and Borg 1.4.5 documentation. Borg 2 changes the repository format and commands; check its own documentation.
  • A full data check reads the whole repository. On large or remote repositories it takes time, adds load and may incur charges.
  • Repair commands change the repository and can lose data. Neither project recommends running them without assessment.
  • Tamper detection depends on encryption and on attackers not having the keys. A repository whose keys sit on the same compromised server does not get that protection (our reading).
  • No commands in this guide were run in a lab. Test every step in an isolated environment first.

Sources

All accessed 14 November 2026.

Related future topics

  • Testing a backup password or key from a machine that is not the backup server
  • Repository repair: assessing damage before you change anything
  • Estimating the cost of full-read verification on cloud object storage