Tech Digest

Backup

restic

Encrypted, deduplicating snapshots to almost any storage target from one static Go binary

BackupBSD-2-ClauseMaturearm64 builds
Last reviewed Profile maintained against the project's own documentation
Operational load
2 / 5
Light
Idle memory
100 MB
typical use ~800 MB
Moving parts
n/a
not container shaped
Backup shape
Mixed
needs a dump or a stop

A command line backup program that writes encrypted, content-addressed, deduplicated snapshots to a local disk, SFTP, S3-compatible object storage or anything rclone can reach. It replaces a scheduled-backup SaaS subscription with a cron job and a bucket you already pay for.

Our verdict on restic#

restic 0.19.1 (5 July 2026) is still a 0.x release after eleven years, and it is still the sane default for encrypted backups to a storage target you do not trust. Compression arrived in 0.14 with repository format 2, and format 2 is now what restic init creates by default, with --compression auto. If your repository predates that you must run migrate upgrade_repo_v2 and then prune --repack-uncompressed, which rewrites the entire repository and costs you a full repo-sized rewrite of traffic and time. The real ongoing cost is memory: restic loads the repository index into RAM, so prune on a multi-terabyte repo with tens of millions of blobs wants several GB and will be OOM-killed on a 2 GB VPS. The second cost is locking. Any run that dies leaves a lock and the next run refuses to start until you restic unlock or pass --retry-lock 10m (added in 0.16.0). Plan for both before you point it at 5 TB.

Who restic is for#

Choose it if

  • Someone who wants one binary in cron backing up a server to a B2 or S3 bucket, with no agent, no daemon and no account to manage.
  • A homelab operator who needs the same tool on Linux, macOS, Windows and a NAS, writing to the same repository format everywhere.
  • Anyone who wants append-only push backups: rest-server with --append-only means a compromised client cannot delete its own history.
  • People who already use rclone for transport and want real deduplicated snapshots on top of it via the rclone: backend.

Look elsewhere if

  • You want a GUI, a dashboard or a schedule you can click. restic ships none of that, and the third-party wrappers are yours to maintain.
  • Your repository will be multi-terabyte and your backup host has under 4 GB of RAM: prune is the operation that will kill you, not the backup.
  • You need the backup tool itself to enforce retention immutably. restic relies on the backend (object lock, append-only rest-server) for that.

What running it actually looks like#

One static Go binary, no daemon, no listening port. You run it from cron or a systemd timer with RESTIC_REPOSITORY and RESTIC_PASSWORD_FILE in the environment and everything else as flags. restic init runs once against the target and writes a config file plus a keys/ entry; nothing else has to exist first. Backends are compiled in: local path, SFTP, REST, S3, Azure, Google Cloud Storage, Backblaze B2, and anything rclone speaks via the rclone: prefix. For push backups from machines that should not hold delete rights, run the separate rest-server (listens on 8000) with --append-only. Official images are restic/restic and ghcr.io/restic/restic, but most people run the host binary. Upgrading is replacing the file; newer restic reads older repositories, so upgrades do not break restores.

Resource profile#

Memory
100 MB idle, around 800 MB in ordinary use. There is no daemon, so these are figures during a run. Memory is dominated by the repository index held in RAM, which scales with blob count, not with how much you are backing up today. Treat both numbers as estimates: a few hundred GB of repo is comfortable in under 1 GB, multi-TB repos with tens of millions of blobs need several GB for `prune`.
CPU and acceleration
Chunking and SHA-256 hashing saturate whatever cores you give it during a backup; `--compression max` roughly doubles CPU per byte. Limit with `GOMAXPROCS` if the backup is competing with a live service.
Storage growth
Deduplicated and compressed, so the repository grows with genuinely new data plus retained snapshot metadata. Pack files default to 16 MiB; temp space needed is pack size times backend connections plus one.
Operational load
2 of 5, Light. One binary on a timer, no service to keep alive, upgrades are a file replacement. The ongoing attention is the maintenance loop: `forget`, `prune`, periodic `check --read-data-subset`, and clearing stale locks when a run gets killed.

Figures describe a small single-household install and are the working assumptions behind our stack planner. Your numbers will differ with library size, user count and hardware. See methodology.

Data and backup surface#

The repository is the backup, so integrity is the whole question. restic check verifies the index, decodes every tree, and confirms each referenced pack file exists at the expected size. It never reads pack contents, so it cannot see bit rot. Only restic check --read-data does, by downloading and re-hashing everything; --read-data-subset=1/10 monthly covers the repository over ten runs at a tenth of the egress. In practice repos are damaged by backends that silently drop or truncate objects, not by restic. Restore throughput is bounded by the backend, so rehearse it: restore one directory to /tmp and time it. Lose the password and the repository is unrecoverable, because the master key exists only wrapped inside keys/. Add a second password with restic key add and store it offline.

Derived backup shape

Mixed. Stop the service, copy its data directory, restart. Check upstream docs for a supported export command first.

Traps and surprises#

prune holds the whole index in RAM

restic loads the repository index into memory, and prune is the peak. Users with terabyte-scale repositories and millions of blobs report needing many gigabytes, well past what a small VPS has. Symptoms are the process being OOM-killed halfway through. Mitigate by pruning on a machine with real RAM, running forget frequently so the pruned set stays small, and using --max-repack-size to bound the work per run.

A killed run leaves an exclusive lock

restic takes a lock for every operation and an exclusive one for prune. If the process is killed, the host sleeps or the network stalls, the lock file stays and the next run aborts. restic unlock removes stale locks and --retry-lock 5m (0.16.0 and later) waits instead of failing. Never wire unlock unconditionally into a script that also runs prune, or you will let two prunes overlap.

Upgrading to repo v2 does not compress old data

migrate upgrade_repo_v2 only flips the format. Existing pack files stay uncompressed and only new backups get compressed, so your repository stays the same size for months. prune --repack-uncompressed fixes it but rewrites every pack, which means downloading and re-uploading the entire repository. On a remote bucket that is a real egress and time bill, so schedule it rather than discovering it.

forget without prune frees nothing

restic forget only removes snapshot references. Space is reclaimed by prune, and until it runs your storage bill does not move. Worse, forget applied without --host or --tag evaluates the retention policy across every snapshot in the repository, so a policy meant for one machine can delete another machine's history. Always scope the policy and always run forget --dry-run first when you change it.

The password is the only key, and it is not escrowed

restic wraps the repository master key with your passphrase and stores the wrapped copy in keys/. There is no recovery path, no vendor and no backdoor. If the passphrase lives only in the environment file of the machine you are backing up, then losing that machine loses the backup too. Run restic key add to create a second passphrase and keep it somewhere the primary host cannot reach.

Specifications#

Category
Backup and object storage
Licence
BSD-2-Clause (Permissive)
Written in
Go
First release
2015
Maturity
Mature
Datastore
Its own content-addressed repository (files in a directory or bucket), no database service
Default ports
none
Ships as
native binary, distro package, docker
arm64 builds
Yes
Identity
Not applicable. No accounts and no web interface. Access control is whatever the storage backend enforces, plus the repository password.
Replaces
Backblaze Personal Backup, CrashPlan, Arq, Carbonite
Project site
restic.net
Source
github.com/restic/restic
Documentation
restic.readthedocs.io/en/stable

Alternatives to restic#

Everything else in backup and object storage, closest in operational weight first.

ToolOps loadIdle RAMLicence
BorgBackup2, Light80 MBBSD-3-Clause
Kopia2, Light200 MBApache-2.0
rclone2, Light40 MBMIT
Duplicati3, Moderate200 MBMIT
Garage3, Moderate150 MBAGPL-3.0-only
MinIO4, Heavy250 MBAGPL-3.0 (repository archived)

Where restic comes up elsewhere#

Sources#

Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.