← Blog
A cream ceramic archive box with a green lid and a coral file tile inside, on a pale lavender background.
Published on
2026-11-09

Restic backup: create a repository and prove you can restore

A Restic backup stores file data in an encrypted repository and records snapshots that you can restore later. Deduplication lets snapshots share data already stored in the repository. The useful first test is small: back up a known set of files, restore one explicit snapshot to a separate directory, and compare the restored contents with a saved baseline.

This guide walks through that test with a local repository. We ran it with Restic 0.19.1 on Windows using synthetic files, including a nested CSV, an empty file, and binary data. Both tested snapshot versions restored correctly. Remote storage, application consistency, permissions, and full-system recovery need separate checks.

Prepare an isolated backup exercise

Install Restic through its official installation instructions, then record the version:

restic version

Create an empty working directory and a subdirectory named source. Put a few disposable files inside it, including a nested directory and an empty file. Run every command below from the working directory. The examples assume restic and Python 3 are available on your command path; use python3 instead of python if that is your installation’s command.

Keep the repository, password material, and restored files outside source. Use a fresh directory named repository for the backup destination. This same-disk exercise teaches the commands; it does not protect against losing the disk. For placement and independent copies, see our cloud backup guide.

Initialize the encrypted repository

restic --repo ./repository init

Enter a unique repository password when prompted and keep a recoverable copy in your password manager. Restic’s repository setup guide explains the password requirement. Losing access to the encryption key means losing access to the backup. A password stored only on the machine you are protecting is a recovery dependency to resolve.

For this exercise, enter the password interactively when requested. Before reusing the commands in a configured shell, review any existing RESTIC_* variables. The explicit repository argument selects the local destination, but other variables can still affect authentication or behavior.

Record the expected files before the backup

Leave the source files unchanged while recording and backing up this baseline. Save the following Python 3 helper as verify-restic.py in the working directory, outside source. It records each regular file’s relative path and SHA-256 hash, then compares a restored tree with that record.

import hashlib
import json
import sys
from pathlib import Path

mode, folder, manifest_file = sys.argv[1:]
root = Path(folder)
if mode not in ('record', 'compare') or not root.is_dir():
    raise SystemExit('Use record or compare with an existing directory.')
files = {}
for path in sorted(root.rglob('*')):
    if path.is_symlink():
        raise SystemExit('This example checks regular files, not symlinks.')
    if path.is_file():
        digest = hashlib.sha256()
        with path.open('rb') as stream:
            for block in iter(lambda: stream.read(1024 * 1024), b''):
                digest.update(block)
        files[path.relative_to(root).as_posix()] = digest.hexdigest()
if mode == 'record':
    with open(manifest_file, 'x', encoding='utf-8') as stream:
        json.dump(files, stream, indent=2)
    print('Recorded', len(files), 'files')
else:
    expected = json.loads(Path(manifest_file).read_text(encoding='utf-8'))
    if files != expected:
        raise SystemExit('FAIL: file paths or SHA-256 hashes differ.')
    print('PASS:', len(files), 'files match')
python verify-restic.py record source baseline.json

The helper creates a new baseline.json and refuses to overwrite an existing one. It checks file bytes and paths, including missing or extra files. It does not validate empty directories, permissions, ownership, timestamps, or application behavior.

Preview the backup, then create a snapshot

Run these commands one at a time. Inspect the preview and stop if the selected files or destination are wrong:

restic --repo ./repository backup source --tag restic-guide --dry-run --verbose=2
restic --repo ./repository backup source --tag restic-guide --force
restic --repo ./repository snapshots

For this small exercise, --force rereads every source file. It is not a requirement for every production run. The backup documentation explains normal change detection, deduplication, and exclusions. A new snapshot can reuse existing data; its existence does not imply a second complete physical copy.

Check the command’s exit status and any errors before proceeding. In Restic’s exit-code reference, backup exit code 3 means some source data could not be read. Treat that as an incomplete backup requiring investigation, even if a snapshot was saved. An unfamiliar nonzero code is also a failure.

Copy the intended snapshot ID from the successful backup output or snapshot list. Replace SNAPSHOT_ID in the remaining examples with that value. Inspect its contents:

restic --repo ./repository ls SNAPSHOT_ID

With the relative source argument used here, the file tree contains /source. Verify the actual listing before restoring. An absolute source path creates a different tree, so do not assume that every repository uses this path.

Check the repository and restore into a fresh directory

restic --repo ./repository check --read-data

Restic’s repository checks distinguish structural checks from reading stored file data. --read-data reads all data packs as well. For a large remote repository, plan for the time, bandwidth, and any applicable retrieval charges.

Choose a restore target that does not exist yet. In this example it is restore-check, beside source. Preview the restore first, then run it only after checking the target:

restic --repo ./repository restore "SNAPSHOT_ID:/source" --target ./restore-check --dry-run --verbose=2
restic --repo ./repository restore "SNAPSHOT_ID:/source" --target ./restore-check --verify

The :/source suffix selects that subtree’s contents, so the restored files go directly into restore-check. --verify verifies restored file contents. An ordinary restore can overwrite existing target files; a fresh target keeps this exercise separate from working data. The restore guide describes subtree selection and overwrite controls.

Use an explicit snapshot ID for a recovery record. latest is convenient but can select a different snapshot after another backup. Restic’s --path selection filter is also different from choosing which subtree to restore.

Compare the restored files with the baseline

python verify-restic.py compare restore-check baseline.json

A successful comparison prints PASS and the file count. A mismatch exits with an error. If the source has changed since the backup, compare against the saved baseline for that snapshot, not the current source directory.

In our local test, the first snapshot contained five files. We changed one file and added another before making the second snapshot. Restoring each snapshot to its own target produced the expected five-file and six-file manifests. Restic’s data check and restore verification passed, and the independent SHA-256 comparisons matched both baselines. The earlier snapshot retained the original file bytes.

These results cover regular files on a local Windows filesystem. They do not establish remote-backend compatibility, recovery permissions, or application correctness. Extend the exercise with the checks that matter to your workload; our recoverability testing guide explains how to define a useful recovery test.

Preview retention before removing recovery points

For this disposable repository, inspect a policy that keeps the latest snapshot in each matching group:

restic --repo ./repository forget --tag restic-guide --keep-last 1 --dry-run

This is a preview, not a recommended production retention policy. With only one snapshot, it has nothing older to remove. Our two-snapshot test confirmed that the dry run left both snapshots available.

Restic’s retention documentation separates forget, which removes snapshot references, from prune, which removes unreferenced data. Default policy groups use host and source paths; inspect the groups and tag scope before approving a real removal. Keep enough recovery points for your recovery needs and test restores before changing retention.

Move from the exercise to a backup job

A production job needs a stable source, protected credentials, failure reporting, a retention policy, and recurring restore tests. For unattended password access, Restic supports a password file or password command. Restrict access to that mechanism, keep it outside the backed-up tree, and document how an authorized operator retrieves the secret after a machine failure.

Do not treat a file copy of a running database as proof of a consistent database backup. Use the database’s supported backup method first, then protect its output. Our PostgreSQL backup and restore guide covers that workflow.

When moving to remote storage, repeat initialization, backup, integrity checks, and restore using the intended endpoint and recovery identity. Verify permissions, connectivity, retention interactions, and costs for that backend. This local test makes no claim that a particular Hivenet service has been validated with Restic.

Your next workload belongs on Hivenet.

Pick one AI, compute, or storage workload and see the difference for yourself. Spin it up in minutes, or let our team map your fastest path to production.