Restic Documentation

Introduction

Restic is a fast and secure backup program. In the following sections, we will present typical workflows, starting with installing, preparing a new repository, and making the first backup.

Quickstart Guide

To get started with a local repository, first define some environment variables:

export RESTIC_REPOSITORY=/srv/restic-repo
export RESTIC_PASSWORD=some-strong-password

Initialize the repository (first time only):

restic init

Create your first backup:

restic backup ~/work

You can list all the snapshots you created with:

restic snapshots

You can restore a backup by noting the snapshot ID you want and running:

restic restore --target /tmp/restore-work your-snapshot-ID

It is a good idea to periodically check your repository’s metadata:

restic check
# or full data:
restic check --read-data

For more details continue reading the next sections.

Installation

Packages

Note that if at any point the package you’re trying to use is outdated, you always have the option to use an official binary from the restic project.

These are up to date binaries, built in a reproducible and verifiable way, that you can download and run without having to do additional installation work.

Please see the Official Binaries section below for various downloads. Official binaries can be updated in place by using the restic self-update command.

Alpine Linux

On Alpine Linux you can install the restic package from the official community repos, e.g. using apk:

$ apk add restic

Arch Linux

On Arch Linux, there is a package called restic installed from the official community repos, e.g. with pacman -S:

$ pacman -S restic

Debian

On Debian, there’s a package called restic which can be installed from the official repos, e.g. with apt-get:

$ apt-get install restic

Fedora

restic can be installed using dnf:

$ dnf install restic

If you used restic from copr previously, remove the copr repo as follows to avoid any conflicts:

$ dnf copr remove copart/restic

macOS

If you are using macOS, you can install restic using Homebrew:

$ brew install restic

On Linux and macOS, you can also install it using pkgx:

$ pkgx install restic

You may also install it using MacPorts:

$ sudo port install restic

Nix & NixOS

If you are using Nix / NixOS there is a package available named restic. It can be installed using nix-env:

$ nix-env --install restic

OpenBSD

On OpenBSD 6.3 and greater, you can install restic using pkg_add:

# pkg_add restic

FreeBSD

On FreeBSD (11 and probably later versions), you can install restic using pkg install:

# pkg install restic

openSUSE

On openSUSE (leap 15.0 and greater, and tumbleweed), you can install restic using the zypper package manager:

# zypper install restic

RHEL & CentOS

For RHEL / CentOS Stream 8 & 9 restic can be installed from the EPEL repository:

$ dnf install epel-release
$ dnf install restic

For RHEL7/CentOS there is a copr repository available, you can try the following:

$ yum install yum-plugin-copr
$ yum copr enable copart/restic
$ yum install restic

If that doesn’t work, you can try adding the repository directly, for CentOS6 use:

$ yum-config-manager --add-repo https://copr.fedorainfracloud.org/coprs/copart/restic/repo/epel-6/copart-restic-epel-6.repo

For CentOS7 use:

$ yum-config-manager --add-repo https://copr.fedorainfracloud.org/coprs/copart/restic/repo/epel-7/copart-restic-epel-7.repo

Solus

restic can be installed from the official repo of Solus via the eopkg package manager:

$ eopkg install restic

Windows

restic can be installed using Scoop:

scoop install restic

Using this installation method, restic.exe will automatically be available in the PATH. It can be called from cmd.exe or PowerShell by typing restic.

Official Binaries

Stable Releases

You can download the latest stable release versions of restic from the restic release page. These builds are considered stable and releases are made regularly in a controlled manner.

There’s both pre-compiled binaries for different platforms as well as the source code available for download. Just download and run the one matching your system.

On your first installation, if you desire, you can verify the integrity of your downloads by testing the SHA-256 checksums listed in SHA256SUMS and verifying the integrity of the file SHA256SUMS with the PGP signature in SHA256SUMS.asc. The PGP signature was created using the key (0x91A6868BD3F7A907):

pub   4096R/91A6868BD3F7A907 2014-11-01
      Key fingerprint = CF8F 18F2 8445 7597 3F79  D4E1 91A6 868B D3F7 A907
      uid                          Alexander Neumann <alexander@bumpern.de>
      sub   4096R/D5FC2ACF4043FDF1 2014-11-01

Once downloaded, the official binaries can be updated in place using the restic self-update command (needs restic 0.9.3 or later):

$ restic version
restic 0.9.3 compiled with go1.11.2 on linux/amd64

$ restic self-update
find latest release of restic at GitHub
latest version is 0.9.4
download file SHA256SUMS
download SHA256SUMS
download file SHA256SUMS
download SHA256SUMS.asc
GPG signature verification succeeded
download restic_0.9.4_linux_amd64.bz2
downloaded restic_0.9.4_linux_amd64.bz2
saved 12115904 bytes in ./restic
successfully updated restic to version 0.9.4

$ restic version
restic 0.9.4 compiled with go1.12.1 on linux/amd64

The self-update command uses the GPG signature on the files uploaded to GitHub to verify their authenticity. No external programs are necessary.

Note

Please be aware that the user executing the restic self-update command must have the permission to replace the restic binary. If you want to save the downloaded restic binary into a different file, pass the file name via the option --output.

Unstable Builds

Another option is to use the latest builds for the master branch, available on the restic beta download site. These too are pre-compiled and ready to run, and a new version is built every time a push is made to the master branch.

Windows

On Windows, put the restic.exe binary into %SystemRoot%\System32 to use restic in scripts without the need for absolute paths to the binary. This requires administrator rights.

Docker Container

We’re maintaining a bare docker container with just a few files and the restic binary, you can get it with docker pull like this:

$ docker pull restic/restic

The container is also available on the GitHub Container Registry:

$ docker pull ghcr.io/restic/restic

Restic relies on the hostname for various operations. Make sure to set a static hostname using –hostname when creating a Docker container, otherwise Docker will assign a random hostname each time.

From Source

restic is written in the Go programming language and you need at least Go version 1.19. Building for Solaris requires at least Go version 1.20. Building restic may also work with older versions of Go, but that’s not supported. See the Getting started guide of the Go project for instructions how to install Go.

In order to build restic from source, execute the following steps:

$ git clone https://github.com/restic/restic
[...]

$ cd restic

$ go run build.go

You can easily cross-compile restic for all supported platforms, just supply the target OS and platform via the command-line options like this (for Windows and FreeBSD respectively):

$ go run build.go --goos windows --goarch amd64

$ go run build.go --goos freebsd --goarch 386

$ go run build.go --goos linux --goarch arm --goarm 6

$ go run build.go --goos solaris --goarch amd64

The resulting binary is statically linked and does not require any libraries.

At the moment, the only tested compiler for restic is the official Go compiler. Building restic with gccgo may work, but is not supported.

Autocompletion

Restic can write out man pages and bash/fish/zsh/powershell compatible autocompletion scripts:

$ ./restic generate --help

The "generate" command writes automatically generated files (like the man pages
and the auto-completion files for bash, fish, zsh and powershell).

Usage:
  restic generate [flags] [command]

Flags:
      --bash-completion file   write bash completion file
      --fish-completion file   write fish completion file
  -h, --help                   help for generate
      --man directory          write man pages to directory
      --powershell-completion  write powershell completion file
      --zsh-completion file    write zsh completion file

Example for using sudo to write a bash completion script directly to the system-wide location:

$ sudo ./restic generate --bash-completion /etc/bash_completion.d/restic
writing bash completion file to /etc/bash_completion.d/restic

Example for using sudo to write a zsh completion script directly to the system-wide location:

$ sudo ./restic generate --zsh-completion /usr/local/share/zsh/site-functions/_restic
writing zsh completion file to /usr/local/share/zsh/site-functions/_restic

Note

The path for the --bash-completion option may vary depending on the operating system used, e.g. /usr/share/bash-completion/completions/restic in Debian and derivatives. Please look up the correct path in the appropriate documentation.

Example for setting up a powershell completion script for the local user’s profile:

# Create profile if one does not exist
PS> If (!(Test-Path $PROFILE.CurrentUserAllHosts)) {New-Item -Path $PROFILE.CurrentUserAllHosts -Force}

PS> $ProfileDir = (Get-Item $PROFILE.CurrentUserAllHosts).Directory

# Generate Restic completions in the same directory as the profile
PS> restic generate --powershell-completion "$ProfileDir\restic-completion.ps1"

# Append to the profile file the command to load Restic completions
PS> Add-Content -Path $PROFILE.CurrentUserAllHosts -Value "`r`nImport-Module $ProfileDir\restic-completion.ps1"

Preparing a new repository

The place where your backups will be saved is called a “repository”. This is simply a directory containing a set of subdirectories and files created by restic to store your backups, some corresponding metadata and encryption keys.

To access the repository, a password (also called a key) must be specified. A repository can hold multiple keys that can all be used to access the repository.

This chapter explains how to create (“init”) such a repository. The repository can be stored locally, or on some remote server or service. We’ll first cover using a local repository; the remaining sections of this chapter cover all the other options. You can skip to the next chapter once you’ve read the relevant section here.

For automated backups, restic supports specifying the repository location in the environment variable RESTIC_REPOSITORY. Restic can also read the repository location from a file specified via the --repository-file option or the environment variable RESTIC_REPOSITORY_FILE.

For automating the supply of the repository password to restic, several options exist:

  • Setting the environment variable RESTIC_PASSWORD

  • Specifying the path to a file with the password via the option --password-file or the environment variable RESTIC_PASSWORD_FILE

  • Configuring a program to be called when the password is needed via the option --password-command or the environment variable RESTIC_PASSWORD_COMMAND

The init command has an option called --repository-version which can be used to explicitly set the version of the new repository. By default, the current stable version is used (see table below). The alias latest will always resolve to the latest repository version. Have a look at the design documentation for more details.

The below table shows which restic version is required to use a certain repository version, as well as notable features introduced in the various versions.

Repository version

Required restic version

Major new features

Comment

1

Any

2

0.14.0 or newer

Compression support

Current default

Local

In order to create a repository at /srv/restic-repo, run the following command and enter the same password twice:

$ restic init --repo /srv/restic-repo
enter password for new repository:
enter password again:
created restic repository 085b3c76b9 at /srv/restic-repo
Please note that knowledge of your password is required to access the repository.
Losing your password means that your data is irrecoverably lost.

Warning

Remembering your password is important! If you lose it, you won’t be able to access data stored in the repository.

Warning

On Linux, storing the backup repository on a CIFS (SMB) share or backing up data from a CIFS share is not recommended due to compatibility issues in older Linux kernels. Either use another backend or set the environment variable GODEBUG to asyncpreemptoff=1. Refer to GitHub issue #2659 for further explanations.

SFTP

In order to backup data via SFTP, you must first set up a server with SSH and let it know your public key. Passwordless login is important since automatic backups are not possible if the server prompts for credentials.

Once the server is configured, the setup of the SFTP repository can simply be achieved by changing the URL scheme in the init command:

$ restic -r sftp:user@host:/srv/restic-repo init
enter password for new repository:
enter password again:
created restic repository f1c6108821 at sftp:user@host:/srv/restic-repo
Please note that knowledge of your password is required to access the repository.
Losing your password means that your data is irrecoverably lost.

You can also specify a relative (read: no slash (/) character at the beginning) directory, in this case the dir is relative to the remote user’s home directory.

Also, if the SFTP server is enforcing domain-confined users, you can specify the user this way: user@domain@host.

Note

Please be aware that SFTP servers do not expand the tilde character (~) normally used as an alias for a user’s home directory. If you want to specify a path relative to the user’s home directory, pass a relative path to the SFTP backend.

If you need to specify a port number or IPv6 address, you’ll need to use URL syntax. E.g., the repository /srv/restic-repo on [::1] (localhost) at port 2222 with username user can be specified as

sftp://user@[::1]:2222//srv/restic-repo

Note the double slash: the first slash separates the connection settings from the path, while the second is the start of the path. To specify a relative path, use one slash.

Alternatively, you can create an entry in the ssh configuration file, usually located in your home directory at ~/.ssh/config or in /etc/ssh/ssh_config:

Host foo
    User bar
    Port 2222

Then use the specified host name foo normally (you don’t need to specify the user name in this case):

$ restic -r sftp:foo:/srv/restic-repo init

You can also add an entry with a special host name which does not exist, just for use with restic, and use the Hostname option to set the real host name:

Host restic-backup-host
    Hostname foo
    User bar
    Port 2222

Then use it in the backend specification:

$ restic -r sftp:restic-backup-host:/srv/restic-repo init

Last, if you’d like to use an entirely different program to create the SFTP connection, you can specify the command to be run with the option -o sftp.command="foobar". Alternatively, -o sftp.args allows setting the arguments passed to the default SSH command (ignored when sftp.command is set)

Note

Please be aware that SFTP servers close connections when no data is received by the client. This can happen when restic is processing huge amounts of unchanged data. To avoid this issue add the following lines to the client’s .ssh/config file:

ServerAliveInterval 60
ServerAliveCountMax 240

REST Server

In order to backup data to the remote server via HTTP or HTTPS protocol, you must first set up a remote REST server instance. Once the server is configured, accessing it is achieved by changing the URL scheme like this:

$ restic -r rest:http://host:8000/ init

Depending on your REST server setup, you can use HTTPS protocol, unix socket, password protection, multiple repositories or any combination of those features. The TCP/IP port is also configurable. Here are some more examples:

$ restic -r rest:https://host:8000/ init
$ restic -r rest:https://user:pass@host:8000/ init
$ restic -r rest:https://user:pass@host:8000/my_backup_repo/ init
$ restic -r rest:http+unix:///tmp/rest.socket:/my_backup_repo/ init

The server username and password can be specified using environment variables as well:

$ export RESTIC_REST_USERNAME=<MY_REST_SERVER_USERNAME>
$ export RESTIC_REST_PASSWORD=<MY_REST_SERVER_PASSWORD>

If you use TLS, restic will use the system’s CA certificates to verify the server certificate. When the verification fails, restic refuses to proceed and exits with an error. If you have your own self-signed certificate, or a custom CA certificate should be used for verification, you can pass restic the certificate filename via the --cacert option. It will then verify that the server’s certificate is contained in the file passed to this option, or signed by a CA certificate in the file. In this case, the system CA certificates are not considered at all.

REST server uses exactly the same directory structure as local backend, so you should be able to access it both locally and via HTTP, even simultaneously.

Amazon S3

Restic can backup data to any Amazon S3 bucket. However, in this case, changing the URL scheme is not enough since Amazon uses special security credentials to sign HTTP requests. By consequence, you must first setup the following environment variables with the credentials you obtained while creating the bucket.

$ export AWS_ACCESS_KEY_ID=<MY_ACCESS_KEY>
$ export AWS_SECRET_ACCESS_KEY=<MY_SECRET_ACCESS_KEY>

You can then easily initialize a repository that uses your Amazon S3 as a backend. If the bucket does not exist it will be created in the default location:

$ restic -r s3:s3.amazonaws.com/bucket_name init
enter password for new repository:
enter password again:
created restic repository eefee03bbd at s3:s3.amazonaws.com/bucket_name
Please note that knowledge of your password is required to access the repository.
Losing your password means that your data is irrecoverably lost.

If needed, you can manually specify the region to use by either setting the environment variable AWS_DEFAULT_REGION or calling restic with an option parameter like -o s3.region="us-east-1". If the region is not specified, the default region is used. Afterwards, the S3 server (at least for AWS, s3.amazonaws.com) will redirect restic to the correct endpoint.

When using temporary credentials make sure to include the session token via then environment variable AWS_SESSION_TOKEN.

Until version 0.8.0, restic used a default prefix of restic, so the files in the bucket were placed in a directory named restic. If you want to access a repository created with an older version of restic, specify the path after the bucket name like this:

$ restic -r s3:s3.amazonaws.com/bucket_name/restic [...]

For an S3-compatible server that is not Amazon (like Minio, see below), or is only available via HTTP, you can specify the URL to the server like this: s3:http://server:port/bucket_name.

Note

restic expects path-style URLs like for example s3.us-west-2.amazonaws.com/bucket_name. Virtual-hosted–style URLs like bucket_name.s3.us-west-2.amazonaws.com, where the bucket name is part of the hostname are not supported. These must be converted to path-style URLs instead, for example s3.us-west-2.amazonaws.com/bucket_name.

Note

Certain S3-compatible servers do not properly implement the ListObjectsV2 API, most notably Ceph versions before v14.2.5. On these backends, as a temporary workaround, you can provide the -o s3.list-objects-v1=true option to use the older ListObjects API instead. This option may be removed in future versions of restic.

Minio Server

Minio is an Open Source Object Storage, written in Go and compatible with Amazon S3 API.

  • Download and Install Minio Download.

  • You can also refer to Minio Docs for step by step guidance on installation and getting started on Minio Client and Minio Server.

You must first setup the following environment variables with the credentials of your Minio Server.

$ export AWS_ACCESS_KEY_ID=<YOUR-MINIO-ACCESS-KEY-ID>
$ export AWS_SECRET_ACCESS_KEY=<YOUR-MINIO-SECRET-ACCESS-KEY>

Now you can easily initialize restic to use Minio server as a backend with this command.

$ ./restic -r s3:http://localhost:9000/restic init
enter password for new repository:
enter password again:
created restic repository 6ad29560f5 at s3:http://localhost:9000/restic1
Please note that knowledge of your password is required to access
the repository. Losing your password means that your data is irrecoverably lost.

Wasabi

Wasabi is a low cost Amazon S3 conformant object storage provider. Due to its S3 conformance, Wasabi can be used as a storage provider for a restic repository.

  • Create a Wasabi bucket using the Wasabi Console.

  • Determine the correct Wasabi service URL for your bucket here.

You must first setup the following environment variables with the credentials of your Wasabi account.

$ export AWS_ACCESS_KEY_ID=<YOUR-WASABI-ACCESS-KEY-ID>
$ export AWS_SECRET_ACCESS_KEY=<YOUR-WASABI-SECRET-ACCESS-KEY>

Now you can easily initialize restic to use Wasabi as a backend with this command.

$ ./restic -r s3:https://<WASABI-SERVICE-URL>/<WASABI-BUCKET-NAME> init
enter password for new repository:
enter password again:
created restic repository xxxxxxxxxx at s3:https://<WASABI-SERVICE-URL>/<WASABI-BUCKET-NAME>
Please note that knowledge of your password is required to access
the repository. Losing your password means that your data is irrecoverably lost.

Alibaba Cloud (Aliyun) Object Storage System (OSS)

Alibaba OSS is an encrypted, secure, cost-effective, and easy-to-use object storage service that enables you to store, back up, and archive large amounts of data in the cloud.

Alibaba OSS is S3 compatible so it can be used as a storage provider for a restic repository with a couple of extra parameters.

  • Determine the correct Alibaba OSS region endpoint - this will be something like oss-eu-west-1.aliyuncs.com

  • You’ll need the region name too - this will be something like oss-eu-west-1

You must first setup the following environment variables with the credentials of your Alibaba OSS account.

$ export AWS_ACCESS_KEY_ID=<YOUR-OSS-ACCESS-KEY-ID>
$ export AWS_SECRET_ACCESS_KEY=<YOUR-OSS-SECRET-ACCESS-KEY>

Now you can easily initialize restic to use Alibaba OSS as a backend with this command.

$ ./restic -o s3.bucket-lookup=dns -o s3.region=<OSS-REGION> -r s3:https://<OSS-ENDPOINT>/<OSS-BUCKET-NAME> init
enter password for new backend:
enter password again:
created restic backend xxxxxxxxxx at s3:https://<OSS-ENDPOINT>/<OSS-BUCKET-NAME>
Please note that knowledge of your password is required to access
the repository. Losing your password means that your data is irrecoverably lost.

For example with an actual endpoint:

$ restic -o s3.bucket-lookup=dns -o s3.region=oss-eu-west-1 -r s3:https://oss-eu-west-1.aliyuncs.com/bucketname init

OpenStack Swift

Restic can backup data to an OpenStack Swift container. Because Swift supports various authentication methods, credentials are passed through environment variables. In order to help integration with existing OpenStack installations, the naming convention of those variables follows the official Python Swift client:

# For keystone v1 authentication
$ export ST_AUTH=<MY_AUTH_URL>
$ export ST_USER=<MY_USER_NAME>
$ export ST_KEY=<MY_USER_PASSWORD>

# For keystone v2 authentication (some variables are optional)
$ export OS_AUTH_URL=<MY_AUTH_URL>
$ export OS_REGION_NAME=<MY_REGION_NAME>
$ export OS_USERNAME=<MY_USERNAME>
$ export OS_PASSWORD=<MY_PASSWORD>
$ export OS_TENANT_ID=<MY_TENANT_ID>
$ export OS_TENANT_NAME=<MY_TENANT_NAME>

# For keystone v3 authentication (some variables are optional)
$ export OS_AUTH_URL=<MY_AUTH_URL>
$ export OS_REGION_NAME=<MY_REGION_NAME>
$ export OS_USERNAME=<MY_USERNAME>
$ export OS_USER_ID=<MY_USER_ID>
$ export OS_PASSWORD=<MY_PASSWORD>
$ export OS_USER_DOMAIN_NAME=<MY_DOMAIN_NAME>
$ export OS_USER_DOMAIN_ID=<MY_DOMAIN_ID>
$ export OS_PROJECT_NAME=<MY_PROJECT_NAME>
$ export OS_PROJECT_DOMAIN_NAME=<MY_PROJECT_DOMAIN_NAME>
$ export OS_PROJECT_DOMAIN_ID=<MY_PROJECT_DOMAIN_ID>
$ export OS_TRUST_ID=<MY_TRUST_ID>

# For keystone v3 application credential authentication (application credential id)
$ export OS_AUTH_URL=<MY_AUTH_URL>
$ export OS_APPLICATION_CREDENTIAL_ID=<MY_APPLICATION_CREDENTIAL_ID>
$ export OS_APPLICATION_CREDENTIAL_SECRET=<MY_APPLICATION_CREDENTIAL_SECRET>

# For keystone v3 application credential authentication (application credential name)
$ export OS_AUTH_URL=<MY_AUTH_URL>
$ export OS_USERNAME=<MY_USERNAME>
$ export OS_USER_DOMAIN_NAME=<MY_DOMAIN_NAME>
$ export OS_APPLICATION_CREDENTIAL_NAME=<MY_APPLICATION_CREDENTIAL_NAME>
$ export OS_APPLICATION_CREDENTIAL_SECRET=<MY_APPLICATION_CREDENTIAL_SECRET>

# For authentication based on tokens
$ export OS_STORAGE_URL=<MY_STORAGE_URL>
$ export OS_AUTH_TOKEN=<MY_AUTH_TOKEN>

Restic should be compatible with an OpenStack RC file in most cases.

Once environment variables are set up, a new repository can be created. The name of the Swift container and optional path can be specified. If the container does not exist, it will be created automatically:

$ restic -r swift:container_name:/path init   # path is optional
enter password for new repository:
enter password again:
created restic repository eefee03bbd at swift:container_name:/path
Please note that knowledge of your password is required to access the repository.
Losing your password means that your data is irrecoverably lost.

The policy of the new container created by restic can be changed using environment variable:

$ export SWIFT_DEFAULT_CONTAINER_POLICY=<MY_CONTAINER_POLICY>

Backblaze B2

Warning

Due to issues with error handling in the current B2 library that restic uses, the recommended way to utilize Backblaze B2 is by using its S3-compatible API.

Follow the documentation to generate S3-compatible access keys and then setup restic as described at Amazon S3. This is expected to work better than using the Backblaze B2 backend directly.

Different from the B2 backend, restic’s S3 backend will only hide no longer necessary files. Thus, make sure to setup lifecycle rules to eventually delete hidden files. The lifecycle setting “Keep only the last version of the file” will keep only the most current version of a file. Read the [Backblaze documentation](https://www.backblaze.com/docs/cloud-storage-lifecycle-rules).

Restic can backup data to any Backblaze B2 bucket. You need to first setup the following environment variables with the credentials you can find in the dashboard on the “Buckets” page when signed into your B2 account:

$ export B2_ACCOUNT_ID=<MY_APPLICATION_KEY_ID>
$ export B2_ACCOUNT_KEY=<MY_APPLICATION_KEY>

To get application keys, a user can go to the App Keys section of the Backblaze account portal. You must create a master application key first. From there, you can generate a standard Application Key. Please note that the Application Key should be treated like a password and will only appear once. If an Application Key is forgotten, you must generate a new one.

For more information on application keys, refer to the Backblaze documentation.

Note

As of version 0.9.2, restic supports both master and non-master application keys. If using a non-master application key, ensure that it is created with at least read and write access to the B2 bucket. On earlier versions of restic, a master application key is required.

You can then initialize a repository stored at Backblaze B2. If the bucket does not exist yet and the credentials you passed to restic have the privilege to create buckets, it will be created automatically:

$ restic -r b2:bucketname:path/to/repo init
enter password for new repository:
enter password again:
created restic repository eefee03bbd at b2:bucketname:path/to/repo
Please note that knowledge of your password is required to access the repository.
Losing your password means that your data is irrecoverably lost.

Note that the bucket name must be unique across all of B2.

The number of concurrent connections to the B2 service can be set with the -o b2.connections=10 switch. By default, at most five parallel connections are established.

Microsoft Azure Blob Storage

You can also store backups on Microsoft Azure Blob Storage. Export the Azure Blob Storage account name:

$ export AZURE_ACCOUNT_NAME=<ACCOUNT_NAME>

For authentication export one of the following variables:

# For storage account key
$ export AZURE_ACCOUNT_KEY=<SECRET_KEY>
# For SAS
$ export AZURE_ACCOUNT_SAS=<SAS_TOKEN>

For authentication using az login set the resource group name and ensure the user has the minimum permissions of the role assignment Storage Blob Data Contributor on Azure RBAC.

$ export AZURE_RESOURCE_GROUP=<RESOURCE_GROUP_NAME>
$ az login

Alternatively, if run on Azure, restic will automatically uses service accounts configured via the standard environment variables or Workload / Managed Identities.

Restic will by default use Azure’s global domain core.windows.net as endpoint suffix. You can specify other suffixes as follows:

$ export AZURE_ENDPOINT_SUFFIX=<ENDPOINT_SUFFIX>

Afterwards you can initialize a repository in a container called foo in the root path like this:

$ restic -r azure:foo:/ init
enter password for new repository:
enter password again:

created restic repository a934bac191 at azure:foo:/
[...]

The number of concurrent connections to the Azure Blob Storage service can be set with the -o azure.connections=10 switch. By default, at most five parallel connections are established.

Google Cloud Storage

Note

Google Cloud Storage is not the same service as Google Drive - to use the latter, please see Other Services via rclone for instructions on using the rclone backend.

Restic supports Google Cloud Storage as a backend and connects via a service account.

For normal restic operation, the service account must have the storage.objects.{create,delete,get,list} permissions for the bucket. These are included in the “Storage Object Admin” role. restic init can create the repository bucket. Doing so requires the storage.buckets.create permission (“Storage Admin” role). If the bucket already exists, that permission is unnecessary.

To use the Google Cloud Storage backend, first create a service account key and download the JSON credentials file. Second, find the Google Project ID that you can see in the Google Cloud Platform console at the “Storage/Settings” menu. Export the path to the JSON key file and the project ID as follows:

$ export GOOGLE_PROJECT_ID=123123123123
$ export GOOGLE_APPLICATION_CREDENTIALS=$HOME/.config/gs-secret-restic-key.json

Restic uses Google’s client library to generate default authentication material, which means if you’re running in Google Container Engine or are otherwise located on an instance with default service accounts then these should work out of the box.

Alternatively, you can specify an existing access token directly:

$ export GOOGLE_ACCESS_TOKEN=ya29.a0AfH6SMC78...

If GOOGLE_ACCESS_TOKEN is set all other authentication mechanisms are disabled. The access token must have at least the https://www.googleapis.com/auth/devstorage.read_write scope. Keep in mind that access tokens are short-lived (usually one hour), so they are not suitable if creating a backup takes longer than that, for instance.

Once authenticated, you can use the gs: backend type to create a new repository in the bucket foo at the root path:

$ restic -r gs:foo:/ init
enter password for new repository:
enter password again:

created restic repository bde47d6254 at gs:foo/
[...]

The number of concurrent connections to the GCS service can be set with the -o gs.connections=10 switch. By default, at most five parallel connections are established.

The region, where a bucket should be created, can be specified with the -o gs.region=us switch. By default, the region is set to us.

Other Services via rclone

The program rclone can be used to access many other different services and store data there. First, you need to install and configure rclone. The general backend specification format is rclone:<remote>:<path>, the <remote>:<path> component will be directly passed to rclone. When you configure a remote named foo, you can then call restic as follows to initiate a new repository in the path bar in the remote foo:

$ restic -r rclone:foo:bar init

Restic takes care of starting and stopping rclone.

Note

If you get an error message saying “cannot implicitly run relative executable rclone found in current directory”, this means that an rclone executable was found in the current directory. For security reasons restic will not run this implicitly, instead you have to use the -o rclone.program=./rclone extended option to override this security check and explicitly tell restic to use the executable.

As a more concrete example, suppose you have configured a remote named b2prod for Backblaze B2 with rclone, with a bucket called yggdrasil. You can then use rclone to list files in the bucket like this:

$ rclone ls b2prod:yggdrasil

In order to create a new repository in the root directory of the bucket, call restic like this:

$ restic -r rclone:b2prod:yggdrasil init

If you want to use the path foo/bar/baz in the bucket instead, pass this to restic:

$ restic -r rclone:b2prod:yggdrasil/foo/bar/baz init

Listing the files of an empty repository directly with rclone should return a listing similar to the following:

$ rclone ls b2prod:yggdrasil/foo/bar/baz
    155 bar/baz/config
    448 bar/baz/keys/4bf9c78049de689d73a56ed0546f83b8416795295cda12ec7fb9465af3900b44

Rclone can be configured with environment variables, so for instance configuring a bandwidth limit for rclone can be achieved by setting the RCLONE_BWLIMIT environment variable:

$ export RCLONE_BWLIMIT=1M

For debugging rclone, you can set the environment variable RCLONE_VERBOSE=2.

The rclone backend has three additional options:

  • -o rclone.program specifies the path to rclone, the default value is just rclone

  • -o rclone.args allows setting the arguments passed to rclone, by default this is serve restic --stdio --b2-hard-delete

  • -o rclone.timeout specifies timeout for waiting on repository opening, the default value is 1m

The reason for the --b2-hard-delete parameters can be found in the corresponding GitHub issue #1657.

In order to start rclone, restic will build a list of arguments by joining the following lists (in this order): rclone.program, rclone.args and as the last parameter the value that follows the rclone: prefix of the repository specification.

So, calling restic like this

$ restic -o rclone.program="/path/to/rclone" \
  -o rclone.args="serve restic --stdio --bwlimit 1M --b2-hard-delete --verbose" \
  -r rclone:b2:foo/bar

runs rclone as follows:

$ /path/to/rclone serve restic --stdio --bwlimit 1M --b2-hard-delete --verbose b2:foo/bar

Manually setting rclone.program also allows running a remote instance of rclone e.g. via SSH on a server, for example:

$ restic -o rclone.program="ssh user@remotehost rclone" -r rclone:b2:foo/bar

With these options, restic works with local files. It uses rclone and credentials stored on remotehost to communicate with B2. All data (except credentials) is encrypted/decrypted locally, then sent/received via remotehost to/from B2.

A more advanced version of this setup forbids specific hosts from removing files in a repository. See the blog post by Simon Ruderich for details and the documentation for the forget command to learn about important security considerations.

The rclone command may also be hard-coded in the SSH configuration or the user’s public key, in this case it may be sufficient to just start the SSH connection (and it’s irrelevant what’s passed after rclone: in the repository specification):

$ restic -o rclone.program="ssh user@host" -r rclone:x

Password prompt on Windows

At the moment, restic only supports the default Windows console interaction. If you use emulation environments like MSYS2 or Cygwin, which use terminals like Mintty or rxvt, you may get a password error.

You can workaround this by using a special tool called winpty (look here and here for detail information). On MSYS2, you can install winpty as follows:

$ pacman -S winpty
$ winpty restic -r /srv/restic-repo init

Group accessible repositories

Since restic version 0.14 local and SFTP repositories can be made accessible to members of a system group. To control this we have to change the group permissions of the top-level config file and restic will use this as a hint to determine what permissions to apply to newly created files. By default restic init sets repositories up to be group inaccessible.

In order to give group members read-only access we simply add the read permission bit to all repository files with chmod:

$ chmod -R g+r /srv/restic-repo

This serves two purposes: 1) it sets the read permission bit on the repository config file triggering restic’s logic to create new files as group accessible and 2) it actually allows the group read access to the files.

Note

By default files on Unix systems are created with a user’s primary group as defined by the gid (group id) field in /etc/passwd. See passwd(5).

For read-write access things are a bit more complicated. When users other than the repository creator add new files in the repository they will be group-owned by this user’s primary group by default, not that of the original repository owner, meaning the original creator wouldn’t have access to these files. That’s hardly what you’d want.

To make this work we can employ the help of the setgid permission bit available on Linux and most other Unix systems. This permission bit makes newly created directories inherit both the group owner (gid) and setgid bit from the parent directory. Setting this bit requires root but since it propagates down to any new directories we only have to do this privileged setup once:

# find /srv/restic-repo -type d -exec chmod g+s '{}' \;
$ chmod -R g+rw /srv/restic-repo

This sets the setgid bit on all existing directories in the repository and then grants read/write permissions for group access.

Note

To manage who has access to the repository you can use usermod on Linux systems, to change which group controls repository access chgrp -R is your friend.

Backing up

Now we’re ready to backup some data. The contents of a directory at a specific point in time is called a “snapshot” in restic. Run the following command and enter the repository password you chose above again:

$ restic -r /srv/restic-repo --verbose backup ~/work
open repository
enter password for repository:
password is correct
lock repository
load index files
start scan
start backup
scan finished in 1.837s
processed 1.720 GiB in 0:12
Files:        5307 new,     0 changed,     0 unmodified
Dirs:         1867 new,     0 changed,     0 unmodified
Added:      1.200 GiB
snapshot 40dc1520 saved

As you can see, restic created a backup of the directory and was pretty fast! The specific snapshot just created is identified by a sequence of hexadecimal characters, 40dc1520 in this case.

You can see that restic tells us it processed 1.720 GiB of data, this is the size of the files and directories in ~/work on the local file system. It also tells us that only 1.200 GiB was added to the repository. This means that some of the data was duplicate and restic was able to efficiently reduce it.

If you don’t pass the --verbose option, restic will print less data. You’ll still get a nice live status display. Be aware that the live status shows the processed files and not the transferred data. Transferred volume might be lower (due to de-duplication) or higher.

On Windows, the --use-fs-snapshot option will use Windows’ Volume Shadow Copy Service (VSS) when creating backups. Restic will transparently create a VSS snapshot for each volume that contains files to backup. Files are read from the VSS snapshot instead of the regular filesystem. This allows to backup files that are exclusively locked by another process during the backup.

By default VSS ignores Outlook OST files. This is not a restriction of restic but the default Windows VSS configuration. The files not to snapshot are configured in the Windows registry under the following key:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\BackupRestore\FilesNotToSnapshot

For more details refer the official Windows documentation e.g. the article Registry Keys and Values for Backup and Restore.

If you run the backup command again, restic will create another snapshot of your data, but this time it’s even faster and no new data was added to the repository (since all data is already there). This is de-duplication at work!

$ restic -r /srv/restic-repo --verbose backup ~/work
open repository
enter password for repository:
password is correct
lock repository
load index files
using parent snapshot d875ae93
start scan
start backup
scan finished in 1.881s
processed 1.720 GiB in 0:03
Files:           0 new,     0 changed,  5307 unmodified
Dirs:            0 new,     0 changed,  1867 unmodified
Added:      0 B
snapshot 79766175 saved

You can even backup individual files in the same repository (not passing --verbose means less output):

$ restic -r /srv/restic-repo backup ~/work.txt
enter password for repository:
password is correct
snapshot 249d0210 saved

If you’re interested in what restic does, pass --verbose twice (or --verbose=2) to display detailed information about each file and directory restic encounters:

$ echo 'more data foo bar' >> ~/work.txt

$ restic -r /srv/restic-repo --verbose --verbose backup ~/work.txt
open repository
enter password for repository:
password is correct
lock repository
load index files
using parent snapshot f3f8d56b
start scan
start backup
scan finished in 2.115s
modified  /home/user/work.txt, saved in 0.007s (22 B added)
modified  /home/user/, saved in 0.008s (0 B added, 378 B metadata)
modified  /home/, saved in 0.009s (0 B added, 375 B metadata)
processed 22 B in 0:02
Files:           0 new,     1 changed,     0 unmodified
Dirs:            0 new,     2 changed,     0 unmodified
Data Blobs:      1 new
Tree Blobs:      3 new
Added:      1.116 KiB
snapshot 8dc503fc saved

In fact several hosts may use the same repository to backup directories and files leading to a greater de-duplication.

Now is a good time to run restic check to verify that all data is properly stored in the repository. You should run this command regularly to make sure the internal structure of the repository is free of errors.

File change detection

When restic encounters a file that has already been backed up, whether in the current backup or a previous one, it makes sure the file’s content is only stored once in the repository. To do so, it normally has to scan the entire content of the file. Because this can be very expensive, restic also uses a change detection rule based on file metadata to determine whether a file is likely unchanged since a previous backup. If it is, the file is not scanned again.

The previous backup snapshot, called “parent” snapshot in restic terminology, is determined as follows. By default restic groups snapshots by hostname and backup paths, and then selects the latest snapshot in the group that matches the current backup. You can change the selection criteria using the --group-by option, which defaults to host,paths. To select the latest snapshot with the same paths independent of the hostname, use paths. Or, to only consider the hostname and tags, use host,tags. Alternatively, it is possible to manually specify a specific parent snapshot using the --parent option. Finally, note that one would normally set the --group-by option for the forget command to the same value.

Change detection is only performed for regular files (not special files, symlinks or directories) that have the exact same path as they did in a previous backup of the same location. If a file or one of its containing directories was renamed, it is considered a different file and its entire contents will be scanned again.

Metadata changes (permissions, ownership, etc.) are always included in the backup, even if file contents are considered unchanged.

On Unix (including Linux and Mac), given that a file lives at the same location as a file in a previous backup, the following file metadata attributes have to match for its contents to be presumed unchanged:

  • Modification timestamp (mtime).

  • Metadata change timestamp (ctime).

  • File size.

  • Inode number (internal number used to reference a file in a filesystem).

The reason for requiring both mtime and ctime to match is that Unix programs can freely change mtime (and some do). In such cases, a ctime change may be the only hint that a file did change.

The following restic backup command line flags modify the change detection rules:

  • --force: turn off change detection and rescan all files.

  • --ignore-ctime: require mtime to match, but allow ctime to differ.

  • --ignore-inode: require mtime to match, but allow inode number

    and ctime to differ.

The option --ignore-inode exists to support FUSE-based filesystems and pCloud, which do not assign stable inodes to files.

Note that the device id of the containing mount point is never taken into account. Device numbers are not stable for removable devices and ZFS snapshots. If you want to force a re-scan in such a case, you can change the mountpoint.

On Windows, a file is considered unchanged when its path, size and modification time match, and only --force has any effect. The other options are recognized but ignored.

Dry Runs

You can perform a backup in dry run mode to see what would happen without modifying the repository.

  • --dry-run/-n Report what would be done, without writing to the repository

Combined with --verbose, you can see a list of changes:

$ restic -r /srv/restic-repo backup ~/work --dry-run -vv | grep "added"
modified  /plan.txt, saved in 0.000s (9.110 KiB added)
modified  /archive.tar.gz, saved in 0.140s (25.542 MiB added)
Would be added to the repository: 25.551 MiB

Excluding Files

You can exclude folders and files by specifying exclude patterns, currently the exclude options are:

  • --exclude Specified one or more times to exclude one or more items

  • --iexclude Same as --exclude but ignores the case of paths

  • --exclude-caches Specified once to exclude a folder’s content if it contains the special CACHEDIR.TAG file, but keep CACHEDIR.TAG.

  • --exclude-file Specified one or more times to exclude items listed in a given file

  • --iexclude-file Same as exclude-file but ignores cases like in --iexclude

  • --exclude-if-present foo Specified one or more times to exclude a folder’s content if it contains a file called foo (optionally having a given header, no wildcards for the file name supported)

  • --exclude-larger-than size Specified once to excludes files larger than the given size

Please see restic help backup for more specific information about each exclude option.

Let’s say we have a file called excludes.txt with the following content:

# exclude go-files
*.go
# exclude foo/x/y/z/bar foo/x/bar foo/bar
foo/**/bar

It can be used like this:

$ restic -r /srv/restic-repo backup ~/work --exclude="*.c" --exclude-file=excludes.txt

This instructs restic to exclude files matching the following criteria:

  • All files matching *.c (parameter --exclude)

  • All files matching *.go (second line in excludes.txt)

  • All files and sub-directories named bar which reside somewhere below a directory called foo (fourth line in excludes.txt)

Patterns use the syntax of the Go function filepath.Match and are tested against the full path of a file/dir to be saved, even if restic is passed a relative path to save. Empty lines and lines starting with a # are ignored.

Environment variables in exclude files are expanded with os.ExpandEnv, so /home/$USER/foo will be expanded to /home/bob/foo for the user bob. To get a literal dollar sign, write $$ to the file - this has to be done even when there’s no matching environment variable for the word following a single $. Note that tilde (~) is not expanded, instead use the $HOME or equivalent environment variable (depending on your operating system).

Patterns need to match on complete path components. For example, the pattern foo:

  • matches /dir1/foo/dir2/file and /dir/foo

  • does not match /dir/foobar or barfoo

A trailing / is ignored, a leading / anchors the pattern at the root directory. This means, /bin matches /bin/bash but does not match /usr/bin/restic.

Regular wildcards cannot be used to match over the directory separator /, e.g. b*ash matches /bin/bash but does not match /bin/ash. For this, the special wildcard ** can be used to match arbitrary sub-directories: The pattern foo/**/bar matches:

  • /dir1/foo/dir2/bar/file

  • /foo/bar/file

  • /tmp/foo/bar

Spaces in patterns listed in an exclude file can be specified verbatim. That is, in order to exclude a file named foo bar star.txt, put that just as it reads on one line in the exclude file. Please note that beginning and trailing spaces are trimmed - in order to match these, use e.g. a * at the beginning or end of the filename.

Spaces in patterns listed in the other exclude options (e.g. --exclude on the command line) are specified in different ways depending on the operating system and/or shell. Restic itself does not need any escaping, but your shell may need some escaping in order to pass the name/pattern as a single argument to restic.

On most Unixy shells, you can either quote or use backslashes. For example:

  • --exclude='foo bar star/foo.txt'

  • --exclude="foo bar star/foo.txt"

  • --exclude=foo\ bar\ star/foo.txt

If a pattern starts with exclamation mark and matches a file that was previously matched by a regular pattern, the match is cancelled. It works similarly to gitignore, with the same limitation: once a directory is excluded, it is not possible to include files inside the directory. Here is a complete example to backup a selection of directories inside the home directory. It works by excluding any directory, then selectively add back some of them.

$HOME/*
!$HOME/Documents
!$HOME/code
!$HOME/.emacs.d
!$HOME/games
# [...]
node_modules
*~
*.o
*.lo
*.pyc

By specifying the option --one-file-system you can instruct restic to only backup files from the file systems the initially specified files or directories reside on. In other words, it will prevent restic from crossing filesystem boundaries and subvolumes when performing a backup.

For example, if you backup / with this option and you have external media mounted under /media/usb then restic will not back up /media/usb at all because this is a different filesystem than /. Virtual filesystems such as /proc are also considered different and thereby excluded when using --one-file-system:

$ restic -r /srv/restic-repo backup --one-file-system /

Please note that this does not prevent you from specifying multiple filesystems on the command line, e.g:

$ restic -r /srv/restic-repo backup --one-file-system / /media/usb

will back up both the / and /media/usb filesystems, but will not include other filesystems like /sys and /proc.

Note

--one-file-system is currently unsupported on Windows, and will cause the backup to immediately fail with an error.

Files larger than a given size can be excluded using the –exclude-larger-than option:

$ restic -r /srv/restic-repo backup ~/work --exclude-larger-than 1M

This excludes files in ~/work which are larger than 1 MiB from the backup.

The default unit for the size value is bytes, so e.g. --exclude-larger-than 2048 would exclude files larger than 2048 bytes (2 KiB). To specify other units, suffix the size value with one of k/K for KiB (1024 bytes), m/M for MiB (1024^2 bytes), g/G for GiB (1024^3 bytes) and t/T for TiB (1024^4 bytes), e.g. 1k, 10K, 20m, 20M, 30g, 30G, 2t or 2T).

Including Files

The options --files-from, --files-from-verbatim and --files-from-raw allow you to give restic a file containing lists of file patterns or paths to be backed up. This is useful e.g. when you want to back up files from many different locations, or when you use some other software to generate the list of files to back up.

The argument passed to --files-from must be the name of a text file that contains one pattern per line. The file must be encoded as UTF-8, or UTF-16 with a byte-order mark. Leading and trailing whitespace is removed from the patterns. Empty lines and lines starting with a # are ignored and each pattern is expanded when read, such that special characters in it are expanded according to the syntax described in the documentation of the Go function filepath.Match.

The argument passed to --files-from-verbatim must be the name of a text file that contains one path per line, e.g. as generated by GNU find with the -print flag. Unlike --files-from, --files-from-verbatim does not expand any special characters in the list of paths, does not strip off any whitespace and does not ignore lines starting with a #. This option simply reads and uses each line as-is, although empty lines are still ignored. Use this option when you want to backup a list of filenames containing the special characters that would otherwise be expanded when using --files-from.

The --files-from-raw option is a variant of --files-from-verbatim that requires each line in the file to be terminated by an ASCII NUL character (the \0 zero byte) instead of a newline, so that it can even handle file paths containing newlines in their name or are not encoded as UTF-8 (except on Windows, where the listed filenames must still be encoded in UTF-8. This option is the safest choice when generating the list of filenames from a script (e.g. GNU find with the -print0 flag).

All three options interpret the argument - as standard input and will read the list of files/patterns from there instead of a text file.

In all cases, paths may be absolute or relative to restic backup’s working directory.

For example, maybe you want to backup files which have a name that matches a certain regular expression pattern (uses GNU find):

$ find /tmp/some_folder -regex PATTERN -print0 > /tmp/files_to_backup

You can then use restic to backup the filtered files:

$ restic -r /srv/restic-repo backup --files-from-raw /tmp/files_to_backup

You can combine all three options with each other and with the normal file arguments:

$ restic backup --files-from /tmp/files_to_backup /tmp/some_additional_file
$ restic backup --files-from /tmp/glob-pattern --files-from-raw /tmp/generated-list /tmp/some_additional_file

Comparing Snapshots

Restic has a diff command which shows the difference between two snapshots and displays a small statistic, just pass the command two snapshot IDs:

$ restic -r /srv/restic-repo diff 5845b002 2ab627a6
password is correct
comparing snapshot ea657ce5 to 2ab627a6:

 C   /restic/cmd_diff.go
+    /restic/foo
 C   /restic/restic

Files:           0 new,     0 removed,     2 changed
Dirs:            1 new,     0 removed
Others:          0 new,     0 removed
Data Blobs:     14 new,    15 removed
Tree Blobs:      2 new,     1 removed
  Added:   16.403 MiB
  Removed: 16.402 MiB

To only compare files in specific subfolders, you can use the <snapshot>:<subfolder> syntax, where snapshot is the ID of a snapshot (or the string latest) and subfolder is a path within the snapshot. For example, to only compare files in the /restic folder, you could use the following command:

$ restic -r /srv/restic-repo diff 5845b002:/restic 2ab627a6:/restic

Backing up special items and metadata

Symlinks are archived as symlinks, restic does not follow them. When you restore, you get the same symlink again, with the same link target and the same timestamps.

If there is a bind-mount below a directory that is to be saved, restic descends into it.

Device files are saved and restored as device files. This means that e.g. /dev/sda is archived as a block device file and restored as such. This also means that the content of the corresponding disk is not read, at least not from the device file.

By default, restic does not save the access time (atime) for any files or other items, since it is not possible to reliably disable updating the access time by restic itself. This means that for each new backup a lot of metadata is written, and the next backup needs to write new metadata again. If you really want to save the access time for files and directories, you can pass the --with-atime option to the backup command.

Note that restic does not back up some metadata associated with files. Of particular note are:

  • File creation date on Unix platforms

  • Inode flags on Unix platforms

  • File ownership and ACLs on Windows

Reading data from a command

Sometimes, it can be useful to directly save the output of a program, for example, mysqldump so that the SQL can later be restored. Restic supports this mode of operation; just supply the option --stdin-from-command when using the backup action, and write the command in place of the files/directories:

$ restic -r /srv/restic-repo backup --stdin-from-command mysqldump [...]

This command creates a new snapshot based on the standard output of mysqldump. By default, the command’s standard output is saved in a file named stdin. A different name can be specified with --stdin-filename:

$ restic -r /srv/restic-repo backup --stdin-filename production.sql --stdin-from-command mysqldump [...]

Restic uses the command exit code to determine whether the command succeeded. A non-zero exit code from the command causes restic to cancel the backup. This causes restic to fail with exit code 1. No snapshot will be created in this case.

Reading data from stdin

Warning

Restic cannot detect if data read from stdin is complete or not. As explained below, this can cause incomplete backup unless additional checks (outside of restic) are configured. If possible, use --stdin-from-command instead.

Alternatively, restic supports reading arbitrary data directly from the standard input. Use the option --stdin of the backup command as follows:

# Will not notice failures, see the warning below
$ gzip bigfile.dat | restic -r /srv/restic-repo backup --stdin

This creates a new snapshot of the content of bigfile.dat. As for --stdin-from-command, the default file name is stdin; a different name can be specified with --stdin-filename.

Important: while it is possible to pipe a command output to restic using --stdin, doing so is discouraged as it will mask errors from the command, leading to corrupted backups. For example, in the following code block, if mysqldump fails to connect to the MySQL database, the restic backup will nevertheless succeed in creating an _empty_ backup:

# Will not notice failures, read the warning above
$ mysqldump [...] | restic -r /srv/restic-repo backup --stdin

A simple solution is to use --stdin-from-command (see above). If you still need to use the --stdin flag, you must use the shell option set -o pipefail (so that a non-zero exit code from one of the programs in the pipe makes the whole chain return a non-zero exit code) and you must check the exit code of the pipe and act accordingly (e.g., remove the last backup). Refer to the Use the Unofficial Bash Strict Mode for more details on this.

Tags for backup

Snapshots can have one or more tags, short strings which add identifying information. Just specify the tags for a snapshot one by one with --tag:

$ restic -r /srv/restic-repo backup --tag projectX --tag foo --tag bar ~/work
[...]

The tags can later be used to keep (or forget) snapshots with the forget command. The command tag can be used to modify tags on an existing snapshot.

Scheduling backups

Restic does not have a built-in way of scheduling backups, as it’s a tool that runs when executed rather than a daemon. There are plenty of different ways to schedule backup runs on various different platforms, e.g. systemd and cron on Linux/BSD and Task Scheduler in Windows, depending on one’s needs and requirements. If you don’t want to implement your own scheduling, you can use resticprofile.

When scheduling restic to run recurringly, please make sure to detect already running instances before starting the backup.

Space requirements

Restic currently assumes that your backup repository has sufficient space for the backup operation you are about to perform. This is a realistic assumption for many cloud providers, but may not be true when backing up to local disks.

Should you run out of space during the middle of a backup, there will be some additional data in the repository, but the snapshot will never be created as it would only be written at the very (successful) end of the backup operation. Previous snapshots will still be there and will still work.

Environment Variables

In addition to command-line options, restic supports passing various options in environment variables. The following lists these environment variables:

RESTIC_REPOSITORY_FILE              Name of file containing the repository location (replaces --repository-file)
RESTIC_REPOSITORY                   Location of repository (replaces -r)
RESTIC_PASSWORD_FILE                Location of password file (replaces --password-file)
RESTIC_PASSWORD                     The actual password for the repository
RESTIC_PASSWORD_COMMAND             Command printing the password for the repository to stdout
RESTIC_KEY_HINT                     ID of key to try decrypting first, before other keys
RESTIC_CACERT                       Location(s) of certificate file(s), comma separated if multiple (replaces --cacert)
RESTIC_TLS_CLIENT_CERT              Location of TLS client certificate and private key (replaces --tls-client-cert)
RESTIC_CACHE_DIR                    Location of the cache directory
RESTIC_COMPRESSION                  Compression mode (only available for repository format version 2)
RESTIC_PROGRESS_FPS                 Frames per second by which the progress bar is updated
RESTIC_PACK_SIZE                    Target size for pack files
RESTIC_READ_CONCURRENCY             Concurrency for file reads

TMPDIR                              Location for temporary files

AWS_ACCESS_KEY_ID                   Amazon S3 access key ID
AWS_SECRET_ACCESS_KEY               Amazon S3 secret access key
AWS_SESSION_TOKEN                   Amazon S3 temporary session token
AWS_DEFAULT_REGION                  Amazon S3 default region
AWS_PROFILE                         Amazon credentials profile (alternative to specifying key and region)
AWS_SHARED_CREDENTIALS_FILE         Location of the AWS CLI shared credentials file (default: ~/.aws/credentials)
RESTIC_AWS_ASSUME_ROLE_ARN          Amazon IAM Role ARN to assume using discovered credentials
RESTIC_AWS_ASSUME_ROLE_SESSION_NAME Session Name to use with the role assumption
RESTIC_AWS_ASSUME_ROLE_EXTERNAL_ID  External ID to use with the role assumption
RESTIC_AWS_ASSUME_ROLE_POLICY       Inline Amazion IAM session policy
RESTIC_AWS_ASSUME_ROLE_REGION       Region to use for IAM calls for the role assumption (default: us-east-1)
RESTIC_AWS_ASSUME_ROLE_STS_ENDPOINT URL to the STS endpoint (default is determined based on RESTIC_AWS_ASSUME_ROLE_REGION). You generally do not need to set this, advanced use only.

AZURE_ACCOUNT_NAME                  Account name for Azure
AZURE_ACCOUNT_KEY                   Account key for Azure
AZURE_ACCOUNT_SAS                   Shared access signatures (SAS) for Azure
AZURE_ENDPOINT_SUFFIX               Endpoint suffix for Azure Storage (default: core.windows.net)

B2_ACCOUNT_ID                       Account ID or applicationKeyId for Backblaze B2
B2_ACCOUNT_KEY                      Account Key or applicationKey for Backblaze B2

GOOGLE_PROJECT_ID                   Project ID for Google Cloud Storage
GOOGLE_APPLICATION_CREDENTIALS      Application Credentials for Google Cloud Storage (e.g. $HOME/.config/gs-secret-restic-key.json)

OS_AUTH_URL                         Auth URL for keystone authentication
OS_REGION_NAME                      Region name for keystone authentication
OS_USERNAME                         Username for keystone authentication
OS_USER_ID                          User ID for keystone v3 authentication
OS_PASSWORD                         Password for keystone authentication
OS_TENANT_ID                        Tenant ID for keystone v2 authentication
OS_TENANT_NAME                      Tenant name for keystone v2 authentication

OS_USER_DOMAIN_NAME                 User domain name for keystone authentication
OS_USER_DOMAIN_ID                   User domain ID for keystone v3 authentication
OS_PROJECT_NAME                     Project name for keystone authentication
OS_PROJECT_DOMAIN_NAME              Project domain name for keystone authentication
OS_PROJECT_DOMAIN_ID                Project domain ID for keystone v3 authentication
OS_TRUST_ID                         Trust ID for keystone v3 authentication

OS_APPLICATION_CREDENTIAL_ID        Application Credential ID (keystone v3)
OS_APPLICATION_CREDENTIAL_NAME      Application Credential Name (keystone v3)
OS_APPLICATION_CREDENTIAL_SECRET    Application Credential Secret (keystone v3)

OS_STORAGE_URL                      Storage URL for token authentication
OS_AUTH_TOKEN                       Auth token for token authentication

RCLONE_BWLIMIT                      rclone bandwidth limit

RESTIC_REST_USERNAME                Restic REST Server username
RESTIC_REST_PASSWORD                Restic REST Server password

ST_AUTH                             Auth URL for keystone v1 authentication
ST_USER                             Username for keystone v1 authentication
ST_KEY                              Password for keystone v1 authentication

See Caching for the rules concerning cache locations when RESTIC_CACHE_DIR is not set.

The external programs that restic may execute include rclone (for rclone backends) and ssh (for the SFTP backend). These may respond to further environment variables and configuration files; see their respective manuals.

Exit status codes

Restic returns one of the following exit status codes after the backup command is run:

  • 0 when the backup was successful (snapshot with all source files created)

  • 1 when there was a fatal error (no snapshot created)

  • 3 when some source files could not be read (incomplete snapshot with remaining files created)

Fatal errors occur for example when restic is unable to write to the backup destination, when there are network connectivity issues preventing successful communication, or when an invalid password or command line argument is provided. When restic returns this exit status code, one should not expect a snapshot to have been created.

Source file read errors occur when restic fails to read one or more files or directories that it was asked to back up, e.g. due to permission problems. Restic displays the number of source file read errors that occurred while running the backup. If there are errors of this type, restic will still try to complete the backup run with all the other files, and create a snapshot that then contains all but the unreadable files.

One can use these exit status codes in scripts and other automation tools, to make them aware of the outcome of the backup run. To manually inspect the exit code in e.g. Linux, run echo $?.

Working with repositories

Listing all snapshots

Now, you can list all the snapshots stored in the repository. The size column only exists for snapshots created using restic 0.17.0 or later. It reflects the size of the contained files at the time when the snapshot was created.

$ restic -r /srv/restic-repo snapshots
enter password for repository:
ID        Date                 Host    Tags   Directory        Size
-------------------------------------------------------------------------
40dc1520  2015-05-08 21:38:30  kasimir        /home/user/work  20.643GiB
79766175  2015-05-08 21:40:19  kasimir        /home/user/work  20.645GiB
bdbd3439  2015-05-08 21:45:17  luigi          /home/art        3.141GiB
590c8fc8  2015-05-08 21:47:38  kazik          /srv             580.200MiB
9f0bc19e  2015-05-08 21:46:11  luigi          /srv             572.180MiB

You can filter the listing by directory path:

$ restic -r /srv/restic-repo snapshots --path="/srv"
enter password for repository:
ID        Date                 Host    Tags   Directory  Size
-------------------------------------------------------------------
590c8fc8  2015-05-08 21:47:38  kazik          /srv       580.200MiB
9f0bc19e  2015-05-08 21:46:11  luigi          /srv       572.180MiB

Or filter by host:

$ restic -r /srv/restic-repo snapshots --host luigi
enter password for repository:
ID        Date                 Host    Tags   Directory  Size
-------------------------------------------------------------------
bdbd3439  2015-05-08 21:45:17  luigi          /home/art  3.141GiB
9f0bc19e  2015-05-08 21:46:11  luigi          /srv       572.180MiB

Combining filters is also possible.

Furthermore you can group the output by the same filters (host, paths, tags):

$ restic -r /srv/restic-repo snapshots --group-by host

enter password for repository:
snapshots for (host [kasimir])
ID        Date                 Host    Tags   Directory        Size
------------------------------------------------------------------------
40dc1520  2015-05-08 21:38:30  kasimir        /home/user/work  20.643GiB
79766175  2015-05-08 21:40:19  kasimir        /home/user/work  20.645GiB
2 snapshots
snapshots for (host [luigi])
ID        Date                 Host    Tags   Directory  Size
-------------------------------------------------------------------
bdbd3439  2015-05-08 21:45:17  luigi          /home/art  3.141GiB
9f0bc19e  2015-05-08 21:46:11  luigi          /srv       572.180MiB
2 snapshots
snapshots for (host [kazik])
ID        Date                 Host    Tags   Directory  Size
-------------------------------------------------------------------
590c8fc8  2015-05-08 21:47:38  kazik          /srv       580.200MiB
1 snapshots

Listing files in a snapshot

To get a list of the files in a specific snapshot you can use the ls command:

$ restic ls 073a90db

snapshot 073a90db of [/home/user/work.txt] filtered by [] at 2024-01-21 16:51:18.474558607 +0100 CET):
/home
/home/user
/home/user/work.txt

The special snapshot ID latest can be used to list files and directories of the latest snapshot in the repository. The --host flag can be used in conjunction to select the latest snapshot originating from a certain host only.

$ restic ls --host kasimir latest

snapshot 073a90db of [/home/user/work.txt] filtered by [] at 2024-01-21 16:51:18.474558607 +0100 CET):
/home
/home/user
/home/user/work.txt

By default, ls prints all files in a snapshot.

File listings can optionally be filtered by directories. Any positional arguments after the snapshot ID are interpreted as absolute directory paths, and only files inside those directories will be listed. Files in subdirectories are not listed when filtering by directories. If the --recursive flag is used, then subdirectories are also included. Any directory paths specified must be absolute (starting with a path separator); paths use the forward slash ‘/’ as separator.

$ restic ls latest /home

snapshot 073a90db of [/home/user/work.txt] filtered by [/home] at 2024-01-21 16:51:18.474558607 +0100 CET):
/home
/home/user
$ restic ls --recursive latest /home

snapshot 073a90db of [/home/user/work.txt] filtered by [/home] at 2024-01-21 16:51:18.474558607 +0100 CET):
/home
/home/user
/home/user/work.txt

To show more details about the files in a snapshot, you can use the --long option. The colums include file permissions, UID, GID, file size, modification time and file path. For scripting usage, the ls command supports the --json flag; the JSON output format is described at ls.

$ restic ls --long latest

snapshot 073a90db of [/home/user/work.txt] filtered by [] at 2024-01-21 16:51:18.474558607 +0100 CET):
drwxr-xr-x     0     0      0 2024-01-21 16:50:52 /home
drwxr-xr-x     0     0      0 2024-01-21 16:51:03 /home/user
-rw-r--r--     0     0     18 2024-01-21 16:51:03 /home/user/work.txt

NCDU (NCurses Disk Usage) is a tool to analyse disk usage of directories. The ls command supports outputting information about a snapshot in the NCDU format using the --ncdu option.

You can use it as follows: restic ls latest --ncdu | ncdu -f -

Copying snapshots between repositories

In case you want to transfer snapshots between two repositories, for example from a local to a remote repository, you can use the copy command:

$ restic -r /srv/restic-repo-copy copy --from-repo /srv/restic-repo
repository d6504c63 opened successfully, password is correct
repository 3dd0878c opened successfully, password is correct

snapshot 410b18a2 of [/home/user/work] at 2020-06-09 23:15:57.305305 +0200 CEST by user@kasimir
  copy started, this may take a while...
snapshot 7a746a07 saved

snapshot 4e5d5487 of [/home/user/work] at 2020-05-01 22:44:07.012113 +0200 CEST by user@kasimir
skipping snapshot 4e5d5487, was already copied to snapshot 50eb62b7

The example command copies all snapshots from the source repository /srv/restic-repo to the destination repository /srv/restic-repo-copy. Snapshots which have previously been copied between repositories will be skipped by later copy runs.

Important

This process will have to both download (read) and upload (write) the entire snapshot(s) due to the different encryption keys used in the source and destination repository. This may incur higher bandwidth usage and costs than expected during normal backup runs.

Important

The copying process does not re-chunk files, which may break deduplication between the files copied and files already stored in the destination repository. This means that copied files, which existed in both the source and destination repository, may occupy up to twice their space in the destination repository. See below for how to avoid this.

The source repository is specified with --from-repo or can be read from a file specified via --from-repository-file. Both of these options can also be set as environment variables $RESTIC_FROM_REPOSITORY or $RESTIC_FROM_REPOSITORY_FILE, respectively. For the source repository the password can be read from a file --from-password-file or from a command --from-password-command. Alternatively the environment variables $RESTIC_FROM_PASSWORD_COMMAND and $RESTIC_FROM_PASSWORD_FILE can be used. It is also possible to directly pass the password via $RESTIC_FROM_PASSWORD. The key which should be used for decryption can be selected by passing its ID via the flag --from-key-hint or the environment variable $RESTIC_FROM_KEY_HINT.

Note

In case the source and destination repository use the same backend, the configuration options and environment variables used to configure the backend may apply to both repositories – for example it might not be possible to specify different accounts for the source and destination repository. You can avoid this limitation by using the rclone backend along with remotes which are configured in rclone.

Note

If copy is aborted, copy will resume the interrupted copying when it is run again. It’s possible that up to 10 minutes of progress can be lost because the repository index is only updated from time to time.

Filtering snapshots to copy

The list of snapshots to copy can be filtered by host, path in the backup and/or a comma-separated tag list:

$ restic -r /srv/restic-repo-copy copy --from-repo /srv/restic-repo --host luigi --path /srv --tag foo,bar

It is also possible to explicitly specify the list of snapshots to copy, in which case only these instead of all snapshots will be copied:

$ restic -r /srv/restic-repo-copy copy --from-repo /srv/restic-repo 410b18a2 4e5d5487 latest

Ensuring deduplication for copied snapshots

Even though the copy command can transfer snapshots between arbitrary repositories, deduplication between snapshots from the source and destination repository may not work. To ensure proper deduplication, both repositories have to use the same parameters for splitting large files into smaller chunks, which requires additional setup steps. With the same parameters restic will for both repositories split identical files into identical chunks and therefore deduplication also works for snapshots copied between these repositories.

The chunker parameters are generated once when creating a new (destination) repository. That is for a copy destination repository we have to instruct restic to initialize it using the same chunker parameters as the source repository:

$ restic -r /srv/restic-repo-copy init --from-repo /srv/restic-repo --copy-chunker-params

Note that it is not possible to change the chunker parameters of an existing repository.

Removing files from snapshots

Snapshots sometimes turn out to include more files that intended. Instead of removing the snapshots entirely and running the corresponding backup commands again (which is not always practical after the fact) it is possible to remove the unwanted files from affected snapshots by rewriting them using the rewrite command:

$ restic -r /srv/restic-repo rewrite --exclude secret-file
repository c881945a opened (repository version 2) successfully, password is correct

snapshot 6160ddb2 of [/home/user/work] at 2022-06-12 16:01:28.406630608 +0200 CEST by user@kasimir
excluding /home/user/work/secret-file
saved new snapshot b6aee1ff

snapshot 4fbaf325 of [/home/user/work] at 2022-05-01 11:22:26.500093107 +0200 CEST by user@kasimir

modified 1 snapshots

$ restic -r /srv/restic-repo rewrite --exclude secret-file 6160ddb2
repository c881945a opened (repository version 2) successfully, password is correct

snapshot 6160ddb2 of [/home/user/work] at 2022-06-12 16:01:28.406630608 +0200 CEST by user@kasimir
excluding /home/user/work/secret-file
new snapshot saved as b6aee1ff

modified 1 snapshots

The options --exclude, --exclude-file, --iexclude and --iexclude-file are supported. They behave the same way as for the backup command, see Excluding Files for details.

It is possible to rewrite only a subset of snapshots by filtering them the same way as for the copy command, see Filtering snapshots to copy.

By default, the rewrite command will keep the original snapshots and create new ones for every snapshot which was modified during rewriting. The new snapshots are marked with the tag rewrite to differentiate them from the original, rewritten snapshots.

Alternatively, you can use the --forget option to immediately remove the original snapshots. In this case, no tag is added to the new snapshots. Please note that this only removes the snapshots and not the actual data stored in the repository. Run the prune command afterwards to remove the now unreferenced data (just like when having used the forget command).

In order to preview the changes which rewrite would make, you can use the --dry-run option. This will simulate the rewriting process without actually modifying the repository. Instead restic will only print the actions it would perform.

Modifying metadata of snapshots

Sometimes it may be desirable to change the metadata of an existing snapshot. Currently, rewriting the hostname and the time of the backup is supported. This is possible using the rewrite command with the option --new-host followed by the desired new hostname or the option --new-time followed by the desired new timestamp.

$ restic rewrite --new-host newhost --new-time "1999-01-01 11:11:11"

repository b7dbade3 opened (version 2, compression level auto)
[0:00] 100.00%  1 / 1 index files loaded

snapshot 8ed674f4 of [/path/to/abc.txt] at 2023-11-27 21:57:52.439139291 +0100 CET by user@kasimir
setting time to 1999-01-01 11:11:11 +0100 CET
setting host to newhost
saved new snapshot c05da643

modified 1 snapshots

Checking integrity and consistency

Imagine your repository is saved on a server that has a faulty hard drive, or even worse, attackers get privileged access and modify the files in your repository with the intention to make you restore malicious data:

$ echo "boom" > /srv/restic-repo/index/de30f3231ca2e6a59af4aa84216dfe2ef7339c549dc11b09b84000997b139628

Trying to restore a snapshot which has been modified as shown above will yield an error:

$ restic -r /srv/restic-repo --no-cache restore c23e491f --target /tmp/restore-work
...
Fatal: unable to load index de30f323: load <index/de30f3231c>: invalid data returned

In order to detect these things before they become a problem, it’s a good idea to regularly use the check command to test whether your repository is healthy and consistent, and that your precious backup data is unharmed. There are two types of checks that can be performed:

  • Structural consistency and integrity, e.g. snapshots, trees and pack files (default)

  • Integrity of the actual data that you backed up (enabled with flags, see below)

To verify the structure of the repository, issue the check command. If the repository is damaged like in the example above, check will detect this and yield the same error as when you tried to restore:

$ restic -r /srv/restic-repo check
...
load indexes
error: error loading index de30f323: load <index/de30f3231c>: invalid data returned
Fatal: LoadIndex returned errors

If the repository structure is intact, restic will show that no errors were found:

$ restic -r /src/restic-repo check
...
load indexes
check all packs
check snapshots, trees and blobs
no errors were found

By default, check creates a new temporary cache directory to verify that the data stored in the repository is intact. To reuse the existing cache, you can use the --with-cache flag.

If the cache directory is not explicitly set, then check creates its temporary cache directory in the temporary directory, see Temporary files. Otherwise, the specified cache directory is used, as described in Caching.

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. To tell restic to also verify the integrity of the pack files in the repository, use the --read-data flag:

$ restic -r /srv/restic-repo check --read-data
...
load indexes
check all packs
check snapshots, trees and blobs
read all data
[0:00] 100.00%  3 / 3 items
duration: 0:00
no errors were found

Note

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.

Alternatively, use the --read-data-subset parameter to check only a subset of the repository pack files at a time. It supports three ways to select a subset. One selects a specific part of pack files, the second and third selects a random subset of the pack files by the given percentage or size.

Use --read-data-subset=n/t to check a specific part of the repository pack files at a time. The parameter takes two values, n and t. When the check command runs, all pack files in the repository are logically divided in t (roughly equal) groups, and only files that belong to group number n are checked. For example, the following commands check all repository pack files over 5 separate invocations:

$ restic -r /srv/restic-repo check --read-data-subset=1/5
$ restic -r /srv/restic-repo check --read-data-subset=2/5
$ restic -r /srv/restic-repo check --read-data-subset=3/5
$ restic -r /srv/restic-repo check --read-data-subset=4/5
$ restic -r /srv/restic-repo check --read-data-subset=5/5

Use --read-data-subset=x% to check a randomly chosen subset of the repository pack files. It takes one parameter, x, the percentage of pack files to check as an integer or floating point number. This will not guarantee to cover all available pack files after sufficient runs, but it is easy to automate checking a small subset of data after each backup. For a floating point value the following command may be used:

$ restic -r /srv/restic-repo check --read-data-subset=2.5%

When checking bigger subsets you most likely want to specify the percentage as an integer:

$ restic -r /srv/restic-repo check --read-data-subset=10%

Use --read-data-subset=nS to check a randomly chosen subset of the repository pack files. It takes one parameter, nS, where ‘n’ is a whole number representing file size and ‘S’ is the unit of file size (K/M/G/T) of pack files to check. Behind the scenes, the specified size will be converted to percentage of the total repository size. The behaviour of the check command following this conversion will be the same as the percentage option above. For a file size value the following command may be used:

$ restic -r /srv/restic-repo check --read-data-subset=50M
$ restic -r /srv/restic-repo check --read-data-subset=10G

Upgrading the repository format version

Repositories created using earlier restic versions use an older repository format version and have to be upgraded to allow using all new features. Upgrading must be done explicitly as a newer repository version increases the minimum restic version required to access the repository. For example the repository format version 2 is only readable using restic 0.14.0 or newer.

Upgrading to repository version 2 is a two step process: first run migrate upgrade_repo_v2 which will check the repository integrity and then upgrade the repository version. Repository problems must be corrected before the migration will be possible. After the migration is complete, run prune to compress the repository metadata. To limit the amount of data rewritten in at once, you can use the prune --max-repack-size size parameter, see Customize pruning for more details.

File contents stored in the repository will not be rewritten, data from new backups will be compressed. Over time more and more of the repository will be compressed. To speed up this process and compress all not yet compressed data, you can run prune --repack-uncompressed. When you plan to create your backups with maximum compression, you should also add the --compression max flag to the prune command. For already backed up data, the compression level cannot be changed later on.

Tuning Backup Parameters

Restic offers a few parameters that allow tuning the backup. The default values should work well in general although specific use cases can benefit from different non-default values. As the restic commands evolve over time, the optimal value for each parameter can also change across restic versions.

Disabling Backup Progress Estimation

When you start a backup, restic will concurrently count the number of files and their total size, which is used to estimate how long it will take. This will cause some extra I/O, which can slow down backups of network file systems or FUSE mounts. To avoid this overhead at the cost of not seeing a progress estimate, use the --no-scan option of the backup command which disables this file scanning.

Backend Connections

Restic uses a global limit for the number of concurrent connections to a backend. This limit can be configured using -o <backend-name>.connections=5, for example for the REST backend the parameter would be -o rest.connections=5. By default restic uses 5 connections for each backend, except for the local backend which uses a limit of 2. The defaults should work well in most cases. For high-latency backends it can be beneficial to increase the number of connections. Please be aware that this increases the resource consumption of restic and that a too high connection count will degrade performance.

CPU Usage

By default, restic uses all available CPU cores. You can set the environment variable GOMAXPROCS to limit the number of used CPU cores. For example to use a single CPU core, use GOMAXPROCS=1. Limiting the number of usable CPU cores, can slightly reduce the memory usage of restic.

Compression

For a repository using at least repository format version 2, you can configure how data is compressed with the option --compression. It can be set to auto (the default, which will compress very fast), max (which will trade backup speed and CPU usage for slightly better compression), or off (which disables compression). Each setting is only applied for the single run of restic. The option can also be set via the environment variable RESTIC_COMPRESSION.

Data Verification

To prevent the upload of corrupted data to the repository, which can happen due to hardware issues or software bugs, restic verifies that generated files can be decoded and contain the correct data beforehand. This increases the CPU usage during backups. If necessary, you can disable this verification using the --no-extra-verify option of the backup command. However, in this case you should verify the repository integrity more actively using restic check --read-data (or the similar --read-data-subset option). Otherwise, data corruption due to hardware issues or software bugs might go unnoticed.

File Read Concurrency

When backing up files from fast storage like NVMe disks, it can be beneficial to increase the read concurrency. This can increase the overall performance of the backup operation by reading more files in parallel. You can specify the concurrency of file reads with the RESTIC_READ_CONCURRENCY environment variable or the --read-concurrency option of the backup command.

Pack Size

In certain instances, such as very large repositories (in the TiB range) or very fast upload connections, it is desirable to use larger pack sizes to reduce the number of files in the repository and improve upload performance. Notable examples are OpenStack Swift and some Google Drive Team accounts, where there are hard limits on the total number of files. Larger pack sizes can also improve the backup speed for a repository stored on a local HDD. This can be achieved by either using the --pack-size option or defining the $RESTIC_PACK_SIZE environment variable. Restic currently defaults to a 16 MiB pack size.

The side effect of increasing the pack size is requiring more disk space for temporary pack files created before uploading. The space must be available in the system default temp directory, unless overwritten by setting the $TMPDIR environment variable. In addition, depending on the backend the memory usage can also increase by a similar amount. Restic requires temporary space according to the pack size, multiplied by the number of backend connections plus one. For example, if the backend uses 5 connections (the default for most backends), with a target pack size of 64 MiB, you’ll need a minimum of 384 MiB of space in the temp directory. A bit of tuning may be required to strike a balance between resource usage at the backup client and the number of pack files in the repository.

Note that larger pack files increase the chance that the temporary pack files are written to disk. An operating system usually caches file write operations in memory and writes them to disk after a short delay. As larger pack files take longer to upload, this increases the chance of these files being written to disk. This can increase disk wear for SSDs.

Feature Flags

Feature flags allow disabling or enabling certain experimental restic features. The flags can be specified via the RESTIC_FEATURES environment variable. The variable expects a comma-separated list of key[=value],key2[=value2] pairs. The key is the name of a feature flag. The value is optional and can contain either the value true (default if omitted) or false. The list of currently available feature flags is shown by the features command.

Restic will return an error if an invalid feature flag is specified. No longer relevant feature flags may be removed in a future restic release. Thus, make sure to no longer specify these flags.

A feature can either be in alpha, beta, stable or deprecated state.

  • An _alpha_ feature is disabled by default and may change in arbitrary ways between restic versions or be removed.

  • A _beta_ feature is enabled by default, but still can change in minor ways or be removed.

  • A _stable_ feature is always enabled and cannot be disabled. This allows for a transition period after which the flag will be removed in a future restic version.

  • A _deprecated_ feature is always disabled and cannot be enabled. The flag will be removed in a future restic version.

Restoring from backup

Restoring from a snapshot

Restoring a snapshot is as easy as it sounds, just use the following command to restore the contents of the latest snapshot to /tmp/restore-work:

$ restic -r /srv/restic-repo restore 79766175 --target /tmp/restore-work
enter password for repository:
restoring <Snapshot of [/home/user/work] at 2015-05-08 21:40:19.884408621 +0200 CEST> to /tmp/restore-work

Use the word latest to restore the last backup. You can also combine latest with the --host and --path filters to choose the last backup for a specific host, path or both.

$ restic -r /srv/restic-repo restore latest --target /tmp/restore-art --path "/home/art" --host luigi
enter password for repository:
restoring <Snapshot of [/home/art] at 2015-05-08 21:45:17.884408621 +0200 CEST> to /tmp/restore-art

Use --exclude and --include to restrict the restore to a subset of files in the snapshot. For example, to restore a single file:

$ restic -r /srv/restic-repo restore 79766175 --target /tmp/restore-work --include /work/foo
enter password for repository:
restoring <Snapshot of [/home/user/work] at 2015-05-08 21:40:19.884408621 +0200 CEST> to /tmp/restore-work

This will restore the file foo to /tmp/restore-work/work/foo.

To only restore a specific subfolder, you can use the <snapshot>:<subfolder> syntax, where snapshot is the ID of a snapshot (or the string latest) and subfolder is a path within the snapshot.

$ restic -r /srv/restic-repo restore 79766175:/work --target /tmp/restore-work --include /foo
enter password for repository:
restoring <Snapshot of [/home/user/work] at 2015-05-08 21:40:19.884408621 +0200 CEST> to /tmp/restore-work

This will restore the file foo to /tmp/restore-work/foo.

You can use the command restic ls latest or restic find foo to find the path to the file within the snapshot. This path you can then pass to --include in verbatim to only restore the single file or directory.

There are case insensitive variants of --exclude and --include called --iexclude and --iinclude. These options will behave the same way but ignore the casing of paths.

Restoring symbolic links on windows is only possible when the user has SeCreateSymbolicLinkPrivilege privilege or is running as admin. This is a restriction of windows not restic.

By default, restic does not restore files as sparse. Use restore --sparse to enable the creation of sparse files if supported by the filesystem. Then restic will restore long runs of zero bytes as holes in the corresponding files. Reading from a hole returns the original zero bytes, but it does not consume disk space. Note that the exact location of the holes can differ from those in the original file, as their location is determined while restoring and is not stored explicitly.

Restore using mount

Browsing your backup as a regular file system is also very easy. First, create a mount point such as /mnt/restic and then use the following command to serve the repository with FUSE:

$ mkdir /mnt/restic
$ restic -r /srv/restic-repo mount /mnt/restic
enter password for repository:
Now serving /srv/restic-repo at /mnt/restic
Use another terminal or tool to browse the contents of this folder.
When finished, quit with Ctrl-c here or umount the mountpoint.

Mounting repositories via FUSE is only possible on Linux, macOS and FreeBSD. On Linux, the fuse kernel module needs to be loaded and the fusermount command needs to be in the PATH. On macOS, you need FUSE for macOS. On FreeBSD, you may need to install FUSE and load the kernel module (kldload fuse).

Restic supports storage and preservation of hard links. However, since hard links exist in the scope of a filesystem by definition, restoring hard links from a fuse mount should be done by a program that preserves hard links. A program that does so is rsync, used with the option --hard-links.

Note

restic mount is mostly useful if you want to restore just a few files out of a snapshot, or to check which files are contained in a snapshot. To restore many files or a whole snapshot, restic restore is the best alternative, often it is significantly faster.

Printing files to stdout

Sometimes it’s helpful to print files to stdout so that other programs can read the data directly. This can be achieved by using the dump command, like this:

$ restic -r /srv/restic-repo dump latest production.sql | mysql

If you have saved multiple different things into the same repo, the latest snapshot may not be the right one. For example, consider the following snapshots in a repository:

$ restic -r /srv/restic-repo snapshots
ID        Date                 Host        Tags        Directory
----------------------------------------------------------------------
562bfc5e  2018-07-14 20:18:01  mopped                  /home/user/file1
bbacb625  2018-07-14 20:18:07  mopped                  /home/other/work
e922c858  2018-07-14 20:18:10  mopped                  /home/other/work
098db9d5  2018-07-14 20:18:13  mopped                  /production.sql
b62f46ec  2018-07-14 20:18:16  mopped                  /home/user/file1
1541acae  2018-07-14 20:18:18  mopped                  /home/other/work
----------------------------------------------------------------------

Here, restic would resolve latest to the snapshot 1541acae, which does not contain the file we’d like to print at all (production.sql). In this case, you can pass restic the snapshot ID of the snapshot you like to restore:

$ restic -r /srv/restic-repo dump 098db9d5 production.sql | mysql

Or you can pass restic a path that should be used for selecting the latest snapshot. The path must match the patch printed in the “Directory” column, e.g.:

$ restic -r /srv/restic-repo dump --path /production.sql latest production.sql | mysql

It is also possible to dump the contents of a whole folder structure to stdout. To retain the information about the files and folders Restic will output the contents in the tar (default) or zip format:

$ restic -r /srv/restic-repo dump latest /home/other/work > restore.tar
$ restic -r /srv/restic-repo dump -a zip latest /home/other/work > restore.zip

The folder content is then contained at /home/other/work within the archive. To include the folder content at the root of the archive, you can use the <snapshot>:<subfolder> syntax:

$ restic -r /srv/restic-repo dump latest:/home/other/work / > restore.tar

It is also possible to dump the contents of a selected snapshot and folder structure to a file using the --target flag.

Removing backup snapshots

All backup space is finite, so restic allows removing old snapshots. This can be done either manually (by specifying a snapshot ID to remove) or by using a policy that describes which snapshots to forget. For all remove operations, two commands need to be called in sequence: forget to remove snapshots, and prune to remove the remaining data that was referenced only by the removed snapshots. The latter can be automated with the --prune option of forget, which runs prune automatically if any snapshots were actually removed.

Pruning snapshots can be a time-consuming process, depending on the number of snapshots and data to process. During a prune operation, the repository is locked and backups cannot be completed. Please plan your pruning so that there’s time to complete it and it doesn’t interfere with regular backup runs.

It is advisable to run restic check after pruning, to make sure you are alerted, should the internal data structures of the repository be damaged.

Remove a single snapshot

The command snapshots can be used to list all snapshots in a repository like this:

$ restic -r /srv/restic-repo snapshots
enter password for repository:
ID        Date                 Host      Tags  Directory
----------------------------------------------------------------------
40dc1520  2015-05-08 21:38:30  kasimir         /home/user/work
79766175  2015-05-08 21:40:19  kasimir         /home/user/work
bdbd3439  2015-05-08 21:45:17  luigi           /home/art
590c8fc8  2015-05-08 21:47:38  kazik           /srv
9f0bc19e  2015-05-08 21:46:11  luigi           /srv

In order to remove the snapshot of /home/art, use the forget command and specify the snapshot ID on the command line:

$ restic -r /srv/restic-repo forget bdbd3439
enter password for repository:
removed snapshot bdbd3439

Afterwards this snapshot is removed:

$ restic -r /srv/restic-repo snapshots
enter password for repository:
ID        Date                 Host     Tags  Directory
----------------------------------------------------------------------
40dc1520  2015-05-08 21:38:30  kasimir        /home/user/work
79766175  2015-05-08 21:40:19  kasimir        /home/user/work
590c8fc8  2015-05-08 21:47:38  kazik          /srv
9f0bc19e  2015-05-08 21:46:11  luigi          /srv

But the data that was referenced by files in this snapshot is still stored in the repository. To cleanup unreferenced data, the prune command must be run:

$ restic -r /srv/restic-repo prune
enter password for repository:
repository 33002c5e opened successfully, password is correct
loading all snapshots...
loading indexes...
finding data that is still in use for 4 snapshots
[0:00] 100.00%  4 / 4 snapshots
searching used packs...
collecting packs for deletion and repacking
[0:00] 100.00%  5 / 5 packs processed

to repack:            69 blobs / 1.078 MiB
this removes:         67 blobs / 1.047 MiB
to delete:             7 blobs / 25.726 KiB
total prune:          74 blobs / 1.072 MiB
remaining:            16 blobs / 38.003 KiB
unused size after prune: 0 B (0.00% of remaining size)

repacking packs
[0:00] 100.00%  2 / 2 packs repacked
rebuilding index
[0:00] 100.00%  3 / 3 packs processed
deleting obsolete index files
[0:00] 100.00%  3 / 3 files deleted
removing 3 old packs
[0:00] 100.00%  3 / 3 files deleted
done

Afterwards the repository is smaller.

You can automate this two-step process by using the --prune switch to forget:

$ restic forget --keep-last 1 --prune
snapshots for host mopped, directories /home/user/work:

keep 1 snapshots:
ID        Date                 Host        Tags        Directory
----------------------------------------------------------------------
4bba301e  2017-02-21 10:49:18  mopped                  /home/user/work

remove 1 snapshots:
ID        Date                 Host        Tags        Directory
----------------------------------------------------------------------
8c02b94b  2017-02-21 10:48:33  mopped                  /home/user/work

1 snapshots have been removed, running prune
loading all snapshots...
loading indexes...
finding data that is still in use for 1 snapshots
[0:00] 100.00%  1 / 1 snapshots
searching used packs...
collecting packs for deletion and repacking
[0:00] 100.00%  5 / 5 packs processed

to repack:           69 blobs / 1.078 MiB
this removes         67 blobs / 1.047 MiB
to delete:            7 blobs / 25.726 KiB
total prune:         74 blobs / 1.072 MiB
remaining:           16 blobs / 38.003 KiB
unused size after prune: 0 B (0.00% of remaining size)

repacking packs
[0:00] 100.00%  2 / 2 packs repacked
rebuilding index
[0:00] 100.00%  3 / 3 packs processed
deleting obsolete index files
[0:00] 100.00%  3 / 3 files deleted
removing 3 old packs
[0:00] 100.00%  3 / 3 files deleted
done

Removing snapshots according to a policy

Removing snapshots manually is tedious and error-prone, therefore restic allows specifying a policy (one or more --keep-* options) for which snapshots to keep. You can for example define how many hourly, daily, weekly, monthly and yearly snapshots to keep, and any other snapshots will be removed.

Warning

If you use an append-only repository with policy-based snapshot removal, some security considerations are important. Please refer to the section below for more information.

Note

You can always use the --dry-run option of the forget command, which instructs restic to not remove anything but instead just print what actions would be performed.

The forget command accepts the following policy options:

  • --keep-last n keep the n last (most recent) snapshots.

  • --keep-hourly n for the last n hours which have one or more snapshots, keep only the most recent one for each hour.

  • --keep-daily n for the last n days which have one or more snapshots, keep only the most recent one for each day.

  • --keep-weekly n for the last n weeks which have one or more snapshots, keep only the most recent one for each week.

  • --keep-monthly n for the last n months which have one or more snapshots, keep only the most recent one for each month.

  • --keep-yearly n for the last n years which have one or more snapshots, keep only the most recent one for each year.

  • --keep-tag keep all snapshots which have all tags specified by this option (can be specified multiple times).

  • --keep-within duration keep all snapshots having a timestamp within the specified duration of the latest snapshot, where duration is a number of years, months, days, and hours. E.g. 2y5m7d3h will keep all snapshots made in the two years, five months, seven days and three hours before the latest (most recent) snapshot.

  • --keep-within-hourly duration keep all hourly snapshots made within the specified duration of the latest snapshot. The duration is specified in the same way as for --keep-within and the method for determining hourly snapshots is the same as for --keep-hourly.

  • --keep-within-daily duration keep all daily snapshots made within the specified duration of the latest snapshot.

  • --keep-within-weekly duration keep all weekly snapshots made within the specified duration of the latest snapshot.

  • --keep-within-monthly duration keep all monthly snapshots made within the specified duration of the latest snapshot.

  • --keep-within-yearly duration keep all yearly snapshots made within the specified duration of the latest snapshot.

Note

All calendar related options (--keep-{hourly,daily,...}) work on natural time boundaries and not relative to when you run forget. Weeks are Monday 00:00 to Sunday 23:59, days 00:00 to 23:59, hours :00 to :59, etc. They also only count hours/days/weeks/etc which have one or more snapshots. A value of -1 will be interpreted as “forever”, i.e. “keep all”.

Note

All duration related options (--keep-{within-,}*) ignore snapshots with a timestamp in the future (relative to when the forget command is run) and these snapshots will hence not be removed.

Note

If there are not enough snapshots to keep one for each duration related --keep-{within-,}* option, the oldest snapshot is kept additionally.

Note

Specifying --keep-tag '' will match untagged snapshots only.

When forget is run with a policy, restic first loads the list of all snapshots and groups them by their host name and paths. The grouping options can be set with --group-by, e.g. using --group-by paths,tags to instead group snapshots by paths and tags. The policy is then applied to each group of snapshots individually. This is a safety feature to prevent accidental removal of unrelated backup sets. To disable grouping and apply the policy to all snapshots regardless of their host, paths and tags, use --group-by '' (that is, an empty value to --group-by). Note that one would normally set the --group-by option for the backup command to the same value.

Additionally, you can restrict the policy to only process snapshots which have a particular hostname with the --host parameter, or tags with the --tag option. When multiple tags are specified, only the snapshots which have all the tags are considered. For example, the following command removes all but the latest snapshot of all snapshots that have the tag foo:

$ restic forget --tag foo --keep-last 1

This command removes all but the last snapshot of all snapshots that have either the foo or bar tag set:

$ restic forget --tag foo --tag bar --keep-last 1

To only keep the last snapshot of all snapshots with both the tag foo and bar set use:

$ restic forget --tag foo,bar --keep-last 1

To ensure only untagged snapshots are considered, specify the empty string ‘’ as the tag.

$ restic forget --tag '' --keep-last 1

Let’s look at a simple example: Suppose you have only made one backup every Sunday for 12 weeks:

$ restic snapshots
repository f00c6e2a opened successfully, password is correct
ID        Time                 Host        Tags        Paths
---------------------------------------------------------------
0a1f9759  2019-09-01 11:00:00  mopped                  /home/user/work
46cfe4d5  2019-09-08 11:00:00  mopped                  /home/user/work
f6b1f037  2019-09-15 11:00:00  mopped                  /home/user/work
eb430a5d  2019-09-22 11:00:00  mopped                  /home/user/work
8cf1cb9a  2019-09-29 11:00:00  mopped                  /home/user/work
5d33b116  2019-10-06 11:00:00  mopped                  /home/user/work
b9553125  2019-10-13 11:00:00  mopped                  /home/user/work
e1a7b58b  2019-10-20 11:00:00  mopped                  /home/user/work
8f8018c0  2019-10-27 11:00:00  mopped                  /home/user/work
59403279  2019-11-03 11:00:00  mopped                  /home/user/work
dfee9fb4  2019-11-10 11:00:00  mopped                  /home/user/work
e1ae2f40  2019-11-17 11:00:00  mopped                  /home/user/work
---------------------------------------------------------------
12 snapshots

Then forget --keep-daily 4 will keep the last four snapshots, for the last four Sundays, and remove the other snapshots:

$ restic forget --keep-daily 4 --dry-run
repository f00c6e2a opened successfully, password is correct
Applying Policy: keep the last 4 daily snapshots
keep 4 snapshots:
ID        Time                 Host        Tags        Reasons         Paths
-------------------------------------------------------------------------------
8f8018c0  2019-10-27 11:00:00  mopped                  daily snapshot  /home/user/work
59403279  2019-11-03 11:00:00  mopped                  daily snapshot  /home/user/work
dfee9fb4  2019-11-10 11:00:00  mopped                  daily snapshot  /home/user/work
e1ae2f40  2019-11-17 11:00:00  mopped                  daily snapshot  /home/user/work
-------------------------------------------------------------------------------
4 snapshots

remove 8 snapshots:
ID        Time                 Host        Tags        Paths
---------------------------------------------------------------
0a1f9759  2019-09-01 11:00:00  mopped                  /home/user/work
46cfe4d5  2019-09-08 11:00:00  mopped                  /home/user/work
f6b1f037  2019-09-15 11:00:00  mopped                  /home/user/work
eb430a5d  2019-09-22 11:00:00  mopped                  /home/user/work
8cf1cb9a  2019-09-29 11:00:00  mopped                  /home/user/work
5d33b116  2019-10-06 11:00:00  mopped                  /home/user/work
b9553125  2019-10-13 11:00:00  mopped                  /home/user/work
e1a7b58b  2019-10-20 11:00:00  mopped                  /home/user/work
---------------------------------------------------------------
8 snapshots

The processed snapshots are evaluated against all --keep-* options but a snapshot only need to match a single option to be kept (the results are ORed). This means that the most recent snapshot on a Sunday would match both hourly, daily and weekly --keep-* options, and possibly more depending on calendar.

For example, suppose you make one backup every day for 100 years. Then forget --keep-daily 7 --keep-weekly 5 --keep-monthly 12 --keep-yearly 75 would keep the most recent 7 daily snapshots and 4 last-day-of-the-week ones (since the 7 dailies already include 1 weekly). Additionally, 12 or 11 last-day-of-the-month snapshots will be kept (depending on whether one of them ends up being the same as a daily or weekly). And finally 75 or 74 last-day-of-the-year snapshots are kept, depending on whether one of them ends up being the same as an already kept snapshot. All other snapshots are removed.

You might want to maintain the same policy as in the example above, but have irregular backups. For example, the 7 snapshots specified with --keep-daily 7 might be spread over a longer period. If what you want is to keep daily snapshots for the last week, weekly for the last month, monthly for the last year and yearly for the last 75 years, you can instead specify forget --keep-within-daily 7d --keep-within-weekly 1m --keep-within-monthly 1y --keep-within-yearly 75y (note that 1w is not a recognized duration, so you will have to specify 7d instead).

For safety reasons, restic refuses to act on an “empty” policy. For example, if one were to specify --keep-last 0 to forget all snapshots in the repository, restic will respond that no snapshots will be removed. To delete all snapshots, use --keep-last 1 and then finally remove the last snapshot manually (by passing the ID to forget).

Security considerations in append-only mode

Note

TL;DR: With append-only repositories, one should specifically use the --keep-within option of the forget command when removing snapshots.

To prevent a compromised backup client from deleting its backups (for example due to a ransomware infection), a repository service/backend can serve the repository in a so-called append-only mode. This means that the repository is served in such a way that it can only be written to and read from, while delete and overwrite operations are denied. Restic’s rest-server features an append-only mode, but few other standard backends do. To support append-only with such backends, one can use rclone as a complement in between the backup client and the backend service.

To remove snapshots and recover the corresponding disk space, the forget and prune commands require full read, write and delete access to the repository. If an attacker has this, the protection offered by append-only mode is naturally void. The usual and recommended setup with append-only repositories is therefore to use a separate and well-secured client whenever full access to the repository is needed, e.g. for administrative tasks such as running forget, prune and other maintenance commands.

However, even with append-only mode active and a separate, well-secured client used for administrative tasks, an attacker who is able to add garbage snapshots to the repository could bring the snapshot list into a state where all the legitimate snapshots risk being deleted by an unsuspecting administrator that runs the forget command with certain --keep-* options, leaving only the attacker’s useless snapshots.

For example, if the forget policy is to keep three weekly snapshots, and the attacker adds an empty snapshot for each of the last three weeks, all with a timestamp (see the backup command’s --time option) slightly more recent than the existing snapshots (but still within the target week), then the next time the repository administrator (or a scheduled job) runs the forget command with this policy, the legitimate snapshots will be removed (since the policy will keep only the most recent snapshot within each week). Even without running prune, recovering data would be messy and some metadata lost.

To avoid this, forget policies applied to append-only repositories should use the --keep-within option, as this will keep not only the attacker’s snapshots but also the legitimate ones. Assuming the system time is correctly set when forget runs, this will allow the administrator to notice problems with the backup or the compromised host (e.g. by seeing more snapshots than usual or snapshots with suspicious timestamps). This is, of course, limited to the specified duration: if forget --keep-within 7d is run 8 days after the last good snapshot, then the attacker can still use that opportunity to remove all legitimate snapshots.

Customize pruning

To understand the custom options, we first explain how the pruning process works:

  1. All snapshots and directories within snapshots are scanned to determine which data is still in use.

  2. For all files in the repository, restic finds out if the file is fully used, partly used or completely unused.

  3. Completely unused files are marked for deletion. Fully used files are kept. A partially used file is either kept or marked for repacking depending on user options.

    Note that for repacking, restic must download the file from the repository storage and re-upload the needed data in the repository. This can be very time-consuming for remote repositories.

  4. After deciding what to do, prune will actually perform the repack, modify the index according to the changes and delete the obsolete files.

The prune command accepts the following options:

  • --max-unused limit allow unused data up to the specified limit within the repository. This allows restic to keep partly used files instead of repacking them.

    The limit can be specified in several ways:

    • As an absolute size (e.g. 200M). If you want to minimize the space used by your repository, pass 0 to this option.

    • As a size relative to the total repository size (e.g. 10%). This means that after prune, at most 10% of the total data stored in the repository may be unused data. If the repository after prune has a size of 500MB, then at most 50MB may be unused.

    • If the string unlimited is passed, there is no limit for partly unused files. This means that as long as some data is still used within a file stored in the repo, restic will just leave it there. Use this if you want to minimize the time and bandwidth used by the prune operation. Note that metadata will still be repacked.

    Restic tries to repack as little data as possible while still ensuring this limit for unused data. The default value is 5%.

  • --max-repack-size size if set limits the total size of files to repack. As prune first stores all repacked files and deletes the obsolete files at the end, this option might be handy if you expect many files to be repacked and fear to run low on storage.

  • --repack-cacheable-only if set to true only files which contain metadata and would be stored in the cache are repacked. Other pack files are not repacked if this option is set. This allows a very fast repacking using only cached data. It can, however, imply that the unused data in your repository exceeds the value given by --max-unused. The default value is false.

  • --dry-run only show what prune would do.

  • --verbose increased verbosity shows additional statistics for prune.

Recovering from “no free space” errors

In some cases when a repository has grown large enough to fill up all disk space or the allocated quota, then prune might fail to free space. prune works in such a way that a repository remains usable no matter at which point the command is interrupted. However, this also means that prune requires some scratch space to work.

In most cases it is sufficient to instruct prune to use as little scratch space as possible by running it as prune --max-repack-size 0. Note that for restic versions before 0.13.0 prune --max-repack-size 1 must be used. Obviously, this can only work if several snapshots have been removed using forget before. This then allows the prune command to actually remove data from the repository. If the command succeeds, but there is still little free space, then remove a few more snapshots and run prune again.

If prune fails to complete, then prune --unsafe-recover-no-free-space SOME-ID is available as a method of last resort. It allows prune to work with little to no free space. However, a failed prune run can cause the repository to become temporarily unusable. Therefore, make sure that you have a stable connection to the repository storage, before running this command. In case the command fails, it may become necessary to manually remove all files from the index/ folder of the repository and run repair index afterwards.

To prevent accidental usages of the --unsafe-recover-no-free-space option it is necessary to first run prune --unsafe-recover-no-free-space SOME-ID and then replace SOME-ID with the requested ID.

Encryption

“The design might not be perfect, but it’s good. Encryption is a first-class feature, the implementation looks sane and I guess the deduplication trade-off is worth it. So… I’m going to use restic for my personal backups.Filippo Valsorda

Manage repository keys

The key command allows you to set multiple access keys or passwords per repository. In fact, you can use the list, add, remove, and passwd (changes a password) sub-commands to manage these keys very precisely:

$ restic -r /srv/restic-repo key list
enter password for repository:
 ID          User        Host        Created
----------------------------------------------------------------------
*eb78040b    username    kasimir   2015-08-12 13:29:57

$ restic -r /srv/restic-repo key add
enter password for repository:
enter password for new key:
enter password again:
saved new key as <Key of username@kasimir, created on 2015-08-12 13:35:05.316831933 +0200 CEST>

$ restic -r /srv/restic-repo key list
enter password for repository:
 ID          User        Host        Created
----------------------------------------------------------------------
 5c657874    username    kasimir   2015-08-12 13:35:05
*eb78040b    username    kasimir   2015-08-12 13:29:57

Note that the currently used key is indicated by an asterisk (*).

Scripting

This is a list of how certain tasks may be accomplished when you use restic via scripts.

Check if a repository is already initialized

You may find a need to check if a repository is already initialized, perhaps to prevent your script from initializing a repository multiple times. The command cat config may be used for this purpose:

$ restic -r /srv/restic-repo cat config
Fatal: unable to open config file: stat /srv/restic-repo/config: no such file or directory
Is there a repository at the following location?
/srv/restic-repo

If a repository does not exist, restic will return a non-zero exit code and print an error message. Note that restic will also return a non-zero exit code if a different error is encountered (e.g.: incorrect password to cat config) and it may print a different error message. If there are no errors, restic will return a zero exit code and print the repository metadata.

JSON output

Restic outputs JSON data to stdout if requested with the --json flag. The structure of that data varies depending on the circumstance. The JSON output of most restic commands are documented here.

Note

Not all commands support JSON output. If a command does not support JSON output, feel free to submit a pull request!

Warning

We try to keep the JSON output backwards compatible. However, new message types or fields may be added at any time. Similarly, enum-like fields for which a fixed list of allowed values is documented may be extended at any time.

Output formats

Currently only the output on stdout is JSON formatted. Errors printed on stderr are still printed as plain text messages. The generated JSON output uses one of the following two formats.

Single JSON document

Several commands output a single JSON document that can be parsed in its entirety. Depending on the command, the output consists of either a single or multiple lines.

JSON lines

Several commands, in particular long running ones or those that generate a large output, use a format also known as JSON lines. It consists of a stream of new-line separated JSON messages. You can determine the nature of the message using the message_type field.

backup

The backup command uses the JSON lines format with the following message types.

Status

message_type

Always “status”

seconds_elapsed

Time since backup started

seconds_remaining

Estimated time remaining

percent_done

Percentage of data backed up (bytes_done/total_bytes)

total_files

Total number of files detected

files_done

Files completed (backed up to repo)

total_bytes

Total number of bytes in backup set

bytes_done

Number of bytes completed (backed up to repo)

error_count

Number of errors

current_files

List of files currently being backed up

Error

message_type

Always “error”

error

Error message

during

What restic was trying to do

item

Usually, the path of the problematic file

Verbose Status

Verbose status provides details about the progress, including details about backed up files.

message_type

Always “verbose_status”

action

Either “new”, “unchanged”, “modified” or “scan_finished”

item

The item in question

duration

How long it took, in seconds

data_size

How big the item is

metadata_size

How big the metadata is

total_files

Total number of files

Summary

Summary is the last output line in a successful backup.

message_type

Always “summary”

files_new

Number of new files

files_changed

Number of files that changed

files_unmodified

Number of files that did not change

dirs_new

Number of new directories

dirs_changed

Number of directories that changed

dirs_unmodified

Number of directories that did not change

data_blobs

Number of data blobs

tree_blobs

Number of tree blobs

data_added

Amount of (uncompressed) data added, in bytes

data_added_packed

Amount of data added (after compression), in bytes

total_files_processed

Total number of files processed

total_bytes_processed

Total number of bytes processed

total_duration

Total time it took for the operation to complete

snapshot_id

ID of the new snapshot

cat

The cat command returns data about various objects in the repository, which are stored in JSON form. Specifying --json or --quiet will suppress any non-JSON messages the command generates.

diff

The diff command uses the JSON lines format with the following message types.

change

message_type

Always “change”

path

Path that has changed

modifier

Type of change, a concatenation of the following characters: “+” = added, “-” = removed, “T” = entry type changed, “M” = file content changed, “U” = metadata changed, “?” = bitrot detected

statistics

message_type

Always “statistics”

source_snapshot

ID of first snapshot

target_snapshot

ID of second snapshot

changed_files

Number of changed files

added

DiffStat object, see below

removed

DiffStat object, see below

DiffStat object

files

Number of changed files

dirs

Number of changed directories

others

Number of changed other directory entries

data_blobs

Number of data blobs

tree_blobs

Number of tree blobs

bytes

Number of bytes

find

The find command outputs a single JSON document containing an array of JSON objects with matches for your search term. These matches are organized by snapshot.

If the --blob or --tree option is passed, then the output is an array of Blob objects.

hits

Number of matches in the snapshot

snapshot

ID of the snapshot

matches

Array of Match objects detailing a match

Match object

path

Object path

permissions

UNIX permissions

type

Object type e.g. file, dir, etc…

atime

Access time

mtime

Modification time

ctime

Change time

name

Object name

user

Name of owner

group

Name of group

inode

Inode number

mode

UNIX file mode, shorthand of permissions

device_id

OS specific device identifier

links

Number of hardlinks

uid

ID of owner

gid

ID of group

size

Size of object in bytes

Blob object

object_type

Either “blob” or “tree”

id

ID of found blob

path

Path in snapshot

parent_tree

Parent tree blob, only set for type “blob”

snapshot

Snapshot ID

time

Snapshot timestamp

forget

The forget command prints a single JSON document containing an array of ForgetGroups. If specific snapshot IDs are specified, then no output is generated.

The prune command does not yet support JSON such that forget --prune results in a mix of JSON and text output.

ForgetGroup

tags

Tags identifying the snapshot group

host

Host identifying the snapshot group

paths

Paths identifying the snapshot group

keep

Array of Snapshot objects that are kept

remove

Array of Snapshot objects that were removed

reasons

Array of Reason objects describing why a snapshot is kept

Snapshot object

time

Timestamp of when the backup was started

parent

ID of the parent snapshot

tree

ID of the root tree blob

paths

List of paths included in the backup

hostname

Hostname of the backed up machine

username

Username the backup command was run as

uid

ID of owner

gid

ID of group

excludes

List of paths and globs excluded from the backup

tags

List of tags for the snapshot in question

program_version

restic version used to create snapshot

id

Snapshot ID

short_id

Snapshot ID, short form

Reason object

snapshot

Snapshot object, including id and short_id fields

matches

Array containing descriptions of the matching criteria

counters

Object containing counters used by the policies

init

The init command uses the JSON lines format, but only outputs a single message.

message_type

Always “initialized”

id

ID of the created repository

repository

URL of the repository

key list

The key list command returns an array of objects with the following structure.

current

Is currently used key?

id

Unique key ID

userName

User who created it

hostName

Name of machine it was created on

created

Timestamp when it was created

ls

The ls command uses the JSON lines format with the following message types. As an exception, the struct_type field is used to determine the message type.

snapshot

message_type

Always “snapshot”

struct_type

Always “snapshot” (deprecated)

time

Timestamp of when the backup was started

parent

ID of the parent snapshot

tree

ID of the root tree blob

paths

List of paths included in the backup

hostname

Hostname of the backed up machine

username

Username the backup command was run as

uid

ID of owner

gid

ID of group

excludes

List of paths and globs excluded from the backup

tags

List of tags for the snapshot in question

id

Snapshot ID

short_id

Snapshot ID, short form

node

message_type

Always “node”

struct_type

Always “node” (deprecated)

name

Node name

type

Node type

path

Node path

uid

UID of node

gid

GID of node

size

Size in bytes

mode

Node mode

atime

Node access time

mtime

Node modification time

ctime

Node creation time

inode

Inode number of node

restore

The restore command uses the JSON lines format with the following message types.

Status

message_type

Always “status”

seconds_elapsed

Time since restore started

percent_done

Percentage of data restored (bytes_restored/total_bytes)

total_files

Total number of files detected

files_restored

Files restored

total_bytes

Total number of bytes in restore set

bytes_restored

Number of bytes restored

Summary

message_type

Always “summary”

seconds_elapsed

Time since restore started

total_files

Total number of files detected

files_restored

Files restored

total_bytes

Total number of bytes in restore set

bytes_restored

Number of bytes restored

snapshots

The snapshots command returns a single JSON object, an array with objects of the structure outlined below.

time

Timestamp of when the backup was started

parent

ID of the parent snapshot

tree

ID of the root tree blob

paths

List of paths included in the backup

hostname

Hostname of the backed up machine

username

Username the backup command was run as

uid

ID of owner

gid

ID of group

excludes

List of paths and globs excluded from the backup

tags

List of tags for the snapshot in question

program_version

restic version used to create snapshot

summary

Snapshot statistics, see “Summary object”

id

Snapshot ID

short_id

Snapshot ID, short form

Summary object

The contained statistics reflect the information at the point in time when the snapshot was created.

backup_start

Time at which the backup was started

backup_end

Time at which the backup was completed

files_new

Number of new files

files_changed

Number of files that changed

files_unmodified

Number of files that did not change

dirs_new

Number of new directories

dirs_changed

Number of directories that changed

dirs_unmodified

Number of directories that did not change

data_blobs

Number of data blobs

tree_blobs

Number of tree blobs

data_added

Amount of (uncompressed) data added, in bytes

data_added_packed

Amount of data added (after compression), in bytes

total_files_processed

Total number of files processed

total_bytes_processed

Total number of bytes processed

stats

The stats command returns a single JSON object.

total_size

Repository size in bytes

total_file_count

Number of files backed up in the repository

total_blob_count

Number of blobs in the repository

snapshots_count

Number of processed snapshots

total_uncompressed_size

Repository size in bytes if blobs were uncompressed

compression_ratio

Factor by which the already compressed data has shrunk due to compression

compression_progress

Percentage of already compressed data

compression_space_saving

Overall space saving due to compression

version

The version command returns a single JSON object.

version

restic version

go_version

Go compile version

go_os

Go OS

go_arch

Go architecture

Troubleshooting

The repository format used by restic is designed to be error resistant. In particular, commands like, for example, backup or prune can be interrupted at any point in time without damaging the repository. You might have to run unlock manually though, but that’s it.

However, a repository might be damaged if some of its files are damaged or lost. This can occur due to hardware failures, accidentally removing files from the repository or bugs in the implementation of restic.

The following steps will help you recover a repository. This guide does not cover all possible types of repository damages. Thus, if the steps do not work for you or you are unsure how to proceed, then ask for help. Please always include the check output discussed in the next section and what steps you’ve taken to repair the repository so far.

  • Forum

  • Our IRC channel #restic on irc.libera.chat

Make sure that you use the latest available restic version. It can contain bugfixes, and improvements to simplify the repair of a repository. It might also contain a fix for your repository problems!

1. Find out what is damaged

The first step is always to check the repository.

$ restic check --read-data

using temporary cache in /tmp/restic-check-cache-1418935501
repository 12345678 opened (version 2, compression level auto)
created new cache in /tmp/restic-check-cache-1418935501
create exclusive lock for repository
load indexes
check all packs
check snapshots, trees and blobs
error for tree 7ef8ebab:
  id 7ef8ebabc59aadda1a237d23ca7abac487b627a9b86508aa0194690446ff71f6 not found in repository
[0:02] 100.00%  7 / 7 snapshots
read all data
[0:05] 100.00%  25 / 25 packs
Fatal: repository contains errors

Note

This will download the whole repository. If retrieving data from the backend is expensive, then omit the --read-data option. Keep a copy of the check output as it might be necessary later on!

If the output contains warnings that the ciphertext verification failed for some blobs in the repository, then please ask for help in the forum or our IRC channel. These errors are often caused by hardware problems which must be investigated and fixed. Otherwise, the backup will be damaged again and again.

Similarly, if a repository is repeatedly damaged, please open an issue on Github as this could indicate a bug somewhere. Please include the check output and additional information that might help locate the problem.

If check detects damaged pack files, it will show instructions on how to repair them using the repair pack command. Use that command instead of the “Repair the index” section in this guide.

2. Backup the repository

Create a full copy of the repository if possible. Or at the very least make a copy of the index and snapshots folders. This will allow you to roll back the repository if the repair procedure fails. If your repository resides in a cloud storage, then you can for example use rclone to make such a copy.

Please disable all regular operations on the repository to prevent unexpected changes. Especially, forget or prune must be disabled as they could remove data unexpectedly.

Warning

If you suspect hardware problems, then you must investigate those first. Otherwise, the repository will soon be damaged again.

Please take the time to understand what the commands described in the following do. If you are unsure, then ask for help in the forum or our IRC channel. Search whether your issue is already known and solved. Please take a look at the forum and Github issues.

3. Repair the index

Note

If the check command tells you to run restic repair pack, then use that command instead. It will repair the damaged pack files and also update the index.

Restic relies on its index to contain correct information about what data is stored in the repository. Thus, the first step to repair a repository is to repair the index:

$ restic repair index

repository a14e5863 opened (version 2, compression level auto)
loading indexes...
getting pack files to read...
removing not found pack file 83ad44f59b05f6bce13376b022ac3194f24ca19e7a74926000b6e316ec6ea5a4
rebuilding index
[0:00] 100.00%  27 / 27 packs processed
deleting obsolete index files
[0:00] 100.00%  3 / 3 files deleted
done

This ensures that no longer existing files are removed from the index. All later steps to repair the repository rely on a correct index. That is, you must always repair the index first!

Please note that it is not recommended to repair the index unless the repository is actually damaged.

4. Run all backups (optional)

With a correct index, the backup command guarantees that newly created snapshots can be restored successfully. It can also heal older snapshots, if the missing data is also contained in the new snapshot.

Therefore, it is recommended to run all your backup tasks again. In some cases, this is enough to fully repair the repository.

5. Remove missing data from snapshots

If your repository is still missing data, then you can use the repair snapshots command to remove all inaccessible data from the snapshots. That is, this will result in a limited amount of data loss. Using the --forget option, the command will automatically remove the original, damaged snapshots.

$ restic repair snapshots --forget

snapshot 6979421e of [/home/user/restic/restic] at 2022-11-02 20:59:18.617503315 +0100 CET by user@host
  file "/restic/internal/fuse/snapshots_dir.go": removed missing content
  file "/restic/internal/restorer/restorer_unix_test.go": removed missing content
  file "/restic/internal/walker/walker.go": removed missing content
saved new snapshot 7b094cea
removed old snapshot 6979421e

modified 1 snapshots

If you did not add the --forget option, then you have to manually delete all modified snapshots using the forget command. In the example above, you’d have to run restic forget 6979421e.

6. Check the repository again

Phew, we’re almost done now. To make sure that the repository has been successfully repaired please run check again.

$ restic check --read-data

using temporary cache in /tmp/restic-check-cache-2569290785
repository a14e5863 opened (version 2, compression level auto)
created new cache in /tmp/restic-check-cache-2569290785
create exclusive lock for repository
load indexes
check all packs
check snapshots, trees and blobs
[0:00] 100.00%  7 / 7 snapshots
read all data
[0:00] 100.00%  25 / 25 packs
no errors were found

If the check command did not complete with no errors were found, then the repository is still damaged. At this point, please ask for help at the forum or our IRC channel #restic on irc.libera.chat.

Examples

Setting up restic with Amazon S3

Preface

This tutorial will show you how to use restic with Amazon S3. It will show you how to navigate the AWS web interface, create an S3 bucket, create a user with access to only this bucket, and finally how to connect restic to this bucket.

Prerequisites

You should already have a restic binary available on your system that you can run. Furthermore, you should also have an account with AWS. You will likely need to provide credit card details for billing purposes, even if you use their free-tier.

Logging into AWS

Point your browser to https://console.aws.amazon.com and log in using your AWS account. You will be presented with the AWS homepage:

AWS Homepage

By using the “Services” button in the upper left corder, a menu of all services provided by AWS can be opened:

AWS Services Menu

For this tutorial, the Simple Storage Service (S3), as well as Identity and Access Management (IAM) are relevant.

Creating the bucket

First, a bucket to store your backups in must be created. Using the “Services” menu, navigate to S3. In case you already have some S3 buckets, you will see a list of them here:

List of S3 Buckets

Click the “Create bucket” button and choose a name and region for your new bucket. For the purpose of this tutorial, the bucket will be named restic-demo and reside in Frankfurt. Because the bucket name space is shared among all AWS users, the name restic-demo may not be available to you. Be creative and choose a unique bucket name.

Create a Bucket

It is not necessary to configure any special properties or permissions of the bucket just yet. Therefore, just finish the wizard without making any further changes:

Review Bucket Creation

The newly created restic-demo bucket will now appear on the list of S3 buckets:

List With New Bucket

Creating a user

Use the “Services” menu of the AWS web interface to navigate to IAM. This will bring you to the IAM homepage. To create a new user, click on the “Users” menu entry on the left:

IAM Home Page

In case you already have set-up users with IAM before, you will see a list of them here. Use the “Add user” button at the top to create a new user:

IAM User List

For this tutorial, the new user will be named restic-demo-user. Feel free to choose your own name that best fits your needs. This user will only ever access AWS through the restic program and not through the web interface. Therefore, “Programmatic access” is selected for “Access type”:

Choose User Name and Access Type

During the next step, permissions can be assigned to the new user. To use this user with restic, it only needs access to the restic-demo bucket. Select “Attach existing policies directly”, which will bring up a list of pre-defined policies below. Afterwards, click the “Create policy” button to create a custom policy:

Assign a Policy

A new browser window or tab will open with the policy wizard. In Amazon IAM, policies are defined as JSON documents. For this tutorial, the “Visual editor” will be used to generate a policy:

Create a New Policy

For restic to work, two permission statements must be created using the visual policy editor. The first statement is set up as follows:

Service: S3
Allow Actions: DeleteObject, GetObject, PutObject
Resources: arn:aws:s3:::restic-demo/*

This statement allows restic to create, read and delete objects inside the S3 bucket named restic-demo. Adjust the bucket’s name to the name of the bucket you created earlier. Next, add a second statement using the “Add additional permissions” button:

Service: S3
Allow Actions: ListBucket, GetBucketLocation
Resource: arn:aws:s3:::restic-demo

Again, substitute restic-demo with the actual name of your bucket. Note that, unlike before, there is no /* after the bucket name. This statement allows restic to list the objects stored in the restic-demo bucket and to query the bucket’s region.

Continue to the next step by clicking the “Review policy” button and enter a name and description for this policy. For this tutorial, the policy will be named restic-demo-policy. Click “Create policy” to finish the process:

Policy Review

Go back to the browser window or tab where you were previously creating the new user. Click the button labeled “Refresh” above the list of policies to make sure the newly created policy is available to you. Afterwards, use the search function to search for the restic-demo-policy. Select this policy using the checkbox on the left. Then, continue to the next step.

Attach Policy to User

The next page will present an overview of the user account that is about to be created. If everything looks good, click “Create user” to complete the process:

User Creation Review

After the user has been created, its access credentials will be displayed. They consist of the “Access key ID” (think user name), and the “Secret access key” (think password). Copy these down to a safe place.

User Credentials

You have now completed the configuration in AWS. Feel free to close your web browser now.

Initializing the restic repository

Open a terminal and make sure you have the restic binary ready. First, choose a password to encrypt your backups with. In this tutorial, apg is used for this purpose:

$ apg -a 1 -m 32 -n 1 -M NCL
I9n7G7G0ZpDWA3GOcJbIuwQCGvGUBkU5

Note this password somewhere safe along with your AWS credentials. Next, the configuration of restic will be placed into environment variables. This will include sensitive information, such as your AWS secret and repository password. Therefore, make sure the next commands do not end up in your shell’s history file. Adjust the contents of the environment variables to fit your bucket’s name, region, and your user’s API credentials.

$ unset HISTFILE
$ export AWS_DEFAULT_REGION="eu-west-1"
$ export RESTIC_REPOSITORY="s3:https://s3.amazonaws.com/restic-demo"
$ export AWS_ACCESS_KEY_ID="AKIAJAJSLTZCAZ4SRI5Q"
$ export AWS_SECRET_ACCESS_KEY="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"
$ export RESTIC_PASSWORD="I9n7G7G0ZpDWA3GOcJbIuwQCGvGUBkU5"

After the environment is set up, restic may be called to initialize the repository:

$ restic init
created restic backend b5c661a86a at s3:https://s3.amazonaws.com/restic-demo

Please note that knowledge of your password is required to access
the repository. Losing your password means that your data is
irrecoverably lost.

restic is now ready to be used with Amazon S3. Try to create a backup:

$ dd if=/dev/urandom bs=1M count=10 of=test.bin
10+0 records in
10+0 records out
10485760 bytes (10 MB, 10 MiB) copied, 0,0891322 s, 118 MB/s

$ restic backup test.bin
scan [/home/philip/restic-demo/test.bin]
scanned 0 directories, 1 files in 0:00
[0:04] 100.00%  2.500 MiB/s  10.000 MiB / 10.000 MiB  1 / 1 items ... ETA 0:00
duration: 0:04, 2.47MiB/s
snapshot 10fdbace saved

$ restic snapshots
ID        Date                 Host        Tags        Directory
----------------------------------------------------------------------
10fdbace  2017-03-26 16:41:50  blackbox                /home/philip/restic-demo/test.bin

A snapshot was created and stored in the S3 bucket. By default backups to Amazon S3 will use the STANDARD storage class. Available storage classes include STANDARD, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, and REDUCED_REDUNDANCY. A different storage class could have been specified in the above command by using -o or --option:

$ restic backup -o s3.storage-class=REDUCED_REDUNDANCY test.bin

This snapshot may now be restored:

$ mkdir restore

$ restic restore 10fdbace --target restore
restoring <Snapshot 10fdbace of [/home/philip/restic-demo/test.bin] at 2017-03-26 16:41:50.201418102 +0200 CEST by philip@blackbox> to restore

$ ls restore/
test.bin

The snapshot was successfully restored. This concludes the tutorial.

Backing up your system without running restic as root

Motivation

Creating a complete backup of a machine requires a privileged process that is able to read all files. On UNIX-like systems this is traditionally the root user. Processes running as root have superpower. They cannot only read all files but do also have the power to modify the system in any possible way.

With great power comes great responsibility. If a process running as root malfunctions, is exploited, or simply configured in a wrong way it can cause any possible damage to the system. This means you only want to run programs as root that you trust completely. And even if you trust a program, it is good and common practice to run it with the least possible privileges.

Capabilities on Linux

Fortunately, Linux has functionality to divide root’s power into single separate capabilities. You can remove these from a process running as root to restrict it. And you can add capabilities to a process running as a normal user, which is what we are going to do.

Full backup without root

To be able to completely backup a system, restic has to read all the files. Luckily Linux knows a capability that allows precisely this. We can assign this single capability to restic and then run it as an unprivileged user.

First we create a new user called restic that is going to create the backups:

root@a3e580b6369d:/# useradd --system --create-home --shell /sbin/nologin restic

Then we download and install the restic binary into the user’s home directory (please adjust the URL to refer to the latest restic version).

root@a3e580b6369d:/# mkdir ~restic/bin
root@a3e580b6369d:/# curl -L https://github.com/restic/restic/releases/download/v0.12.1/restic_0.12.1_linux_amd64.bz2 | bunzip2 > ~restic/bin/restic

Before we assign any special capability to the restic binary we restrict its permissions so that only root and the newly created restic user can execute it. Otherwise another - possibly untrusted - user could misuse the privileged restic binary to circumvent file access controls.

root@a3e580b6369d:/# chown root:restic ~restic/bin/restic
root@a3e580b6369d:/# chmod 750 ~restic/bin/restic

Finally we can use setcap to add an extended attribute to the restic binary. On every execution the system will read the extended attribute, interpret it and assign capabilities accordingly.

root@a3e580b6369d:/# setcap cap_dac_read_search=+ep ~restic/bin/restic

Important

The capabilities of the setcap command only applies to this specific copy of the restic binary. If you run restic self-update or in any other way replace or update the binary, the capabilities you added above will not be in effect for the new binary. To mitigate this, simply run the setcap command again, to make sure that the new binary has the same and intended capabilities.

From now on the user restic can run restic to backup the whole system.

root@a3e580b6369d:/# sudo -u restic /home/restic/bin/restic --exclude={/dev,/media,/mnt,/proc,/run,/sys,/tmp,/var/tmp} -r /tmp backup /

Participating

Debug Logs

Set the environment variable DEBUG_LOG to let restic write extensive debug messages to the specified filed, e.g.:

$ DEBUG_LOG=/tmp/restic-debug.log restic backup ~/work

If you suspect that there is a bug, you can have a look at the debug log. Please be aware that the debug log might contain sensitive information such as file and directory names.

The debug log will always contain all log messages restic generates. You can also instruct restic to print some or all debug messages to stderr. These can also be limited to e.g. a list of source files or a list of patterns for function names. The patterns are globbing patterns (see the documentation for filepath.Match). Multiple patterns are separated by commas. Patterns are case sensitive.

Printing all log messages to the console can be achieved by setting the file filter to *:

$ DEBUG_FILES=* restic check

If you want restic to just print all debug log messages from the files main.go and lock.go, set the environment variable DEBUG_FILES like this:

$ DEBUG_FILES=main.go,lock.go restic check

The following command line instructs restic to only print debug statements originating in functions that match the pattern *unlock* (case sensitive):

$ DEBUG_FUNCS=*unlock* restic check

Debugging

The program can be built with debug support like this:

$ go run build.go -tags debug

This will make the restic debug <subcommand> available which can be used to inspect internal data structures. In addition, this enables profiling support which can help with investigation performance and memory usage issues.

Contributing

Contributions are welcome! Please open an issue first (or add a comment to an existing issue) if you plan to work on any code or add a new feature. This way, duplicate work is prevented and we can discuss your ideas and design first.

More information and a description of the development environment can be found in CONTRIBUTING.md. A document describing the design of restic and the data structures stored on the back end is contained in Design.

If you’d like to start contributing to restic, but don’t know exactly what do to, have a look at this great article by Dave Cheney: Suggestions for contributing to an Open Source project. A few issues have been tagged with the label help wanted, you can start looking at those.

Security

Important: If you discover something that you believe to be a possible critical security problem, please do not open a GitHub issue but send an email directly to alexander@bumpern.de. If possible, please encrypt your email using the following PGP key (0x91A6868BD3F7A907):

pub   4096R/91A6868BD3F7A907 2014-11-01
      Key fingerprint = CF8F 18F2 8445 7597 3F79  D4E1 91A6 868B D3F7 A907
      uid                          Alexander Neumann <alexander@bumpern.de>
      sub   4096R/D5FC2ACF4043FDF1 2014-11-01

Compatibility

Backward compatibility for backups is important so that our users are always able to restore saved data. Therefore restic follows Semantic Versioning to clearly define which versions are compatible. The repository and data structures contained therein are considered the “Public API” in the sense of Semantic Versioning. This goes for all released versions of restic, this may not be the case for the master branch.

We guarantee backward compatibility of all repositories within one major version; as long as we do not increment the major version, data can be read and restored. We strive to be fully backward compatible to all prior versions.

Building documentation

The restic documentation is built with Sphinx, therefore building it locally requires a recent Python version and requirements listed in doc/requirements.txt. This example will guide you through the process using virtualenv:

$ virtualenv venv # create virtual python environment
$ source venv/bin/activate # activate the virtual environment
$ cd doc
$ pip install -r requirements.txt # install dependencies
$ make html # build html documentation
$ # open _build/html/index.html with your favorite browser

References

Design

Terminology

This section introduces terminology used in this document.

Repository: All data produced during a backup is sent to and stored in a repository in a structured form, for example in a file system hierarchy with several subdirectories. A repository implementation must be able to fulfill a number of operations, e.g. list the contents.

Blob: A Blob combines a number of data bytes with identifying information like the SHA-256 hash of the data and its length.

Pack: A Pack combines one or more Blobs, e.g. in a single file.

Snapshot: A Snapshot stands for the state of a file or directory that has been backed up at some point in time. The state here means the content and meta data like the name and modification time for the file or the directory and its contents.

Storage ID: A storage ID is the SHA-256 hash of the content stored in the repository. This ID is required in order to load the file from the repository.

Repository Format

All data is stored in a restic repository. A repository is able to store data of several different types, which can later be requested based on an ID. This so-called “storage ID” is the SHA-256 hash of the content of a file. All files in a repository are only written once and never modified afterwards. Writing should occur atomically to prevent concurrent operations from reading incomplete files. This allows accessing and even writing to the repository with multiple clients in parallel. Only the prune operation removes data from the repository.

Repositories consist of several directories and a top-level file called config. For all other files stored in the repository, the name for the file is the lower case hexadecimal representation of the storage ID, which is the SHA-256 hash of the file’s contents. This allows for easy verification of files for accidental modifications, like disk read errors, by simply running the program sha256sum on the file and comparing its output to the file name. If the prefix of a filename is unique amongst all the other files in the same directory, the prefix may be used instead of the complete filename.

Apart from the files stored within the keys and data directories, all files are encrypted with AES-256 in counter mode (CTR). The integrity of the encrypted data is secured by a Poly1305-AES message authentication code (MAC). Files in the data directory (“pack files”) consist of multiple parts which are all independently encrypted and authenticated, see below.

In the first 16 bytes of each encrypted file the initialisation vector (IV) is stored. It is followed by the encrypted data and completed by the 16 byte MAC. The format is: IV || CIPHERTEXT || MAC. The complete encryption overhead is 32 bytes. For each file, a new random IV is selected.

The file config is encrypted this way and contains a JSON document like the following:

{
  "version": 2,
  "id": "5956a3f67a6230d4a92cefb29529f10196c7d92582ec305fd71ff6d331d6271b",
  "chunker_polynomial": "25b468838dcb75"
}

After decryption, restic first checks that the version field contains a version number that it understands, otherwise it aborts. At the moment, the version is expected to be 1 or 2. The list of changes in the repository format is contained in the section “Changes” below.

The field id holds a unique ID which consists of 32 random bytes, encoded in hexadecimal. This uniquely identifies the repository, regardless if it is accessed via a remote storage backend or locally. The field chunker_polynomial contains a parameter that is used for splitting large files into smaller chunks (see below).

Repository Layout

The local and sftp backends are implemented using files and directories stored in a file system. The directory layout is the same for both backend types and is also used for all other remote backends.

The basic layout of a repository is shown here:

/tmp/restic-repo
├── config
├── data
│   ├── 21
│   │   └── 2159dd48f8a24f33c307b750592773f8b71ff8d11452132a7b2e2a6a01611be1
│   ├── 32
│   │   └── 32ea976bc30771cebad8285cd99120ac8786f9ffd42141d452458089985043a5
│   ├── 59
│   │   └── 59fe4bcde59bd6222eba87795e35a90d82cd2f138a27b6835032b7b58173a426
│   ├── 73
│   │   └── 73d04e6125cf3c28a299cc2f3cca3b78ceac396e4fcf9575e34536b26782413c
│   [...]
├── index
│   ├── c38f5fb68307c6a3e3aa945d556e325dc38f5fb68307c6a3e3aa945d556e325d
│   └── ca171b1b7394d90d330b265d90f506f9984043b342525f019788f97e745c71fd
├── keys
│   └── b02de829beeb3c01a63e6b25cbd421a98fef144f03b9a02e46eff9e2ca3f0bd7
├── locks
├── snapshots
│   └── 22a5af1bdc6e616f8a29579458c49627e01b32210d09adb288d1ecda7c5711ec
└── tmp

A local repository can be initialized with the restic init command, e.g.:

$ restic -r /tmp/restic-repo init

The local and sftp backends will auto-detect and accept all layouts described in the following sections, so that remote repositories mounted locally e.g. via fuse can be accessed. The layout auto-detection can be overridden by specifying the option -o local.layout=default, valid values are default and s3legacy. The option for the sftp backend is named sftp.layout, for the s3 backend s3.layout.

S3 Legacy Layout

Unfortunately during development the Amazon S3 backend uses slightly different paths (directory names use singular instead of plural for key, lock, and snapshot files), and the pack files are stored directly below the data directory. The S3 Legacy repository layout looks like this:

/config
/data
 ├── 2159dd48f8a24f33c307b750592773f8b71ff8d11452132a7b2e2a6a01611be1
 ├── 32ea976bc30771cebad8285cd99120ac8786f9ffd42141d452458089985043a5
 ├── 59fe4bcde59bd6222eba87795e35a90d82cd2f138a27b6835032b7b58173a426
 ├── 73d04e6125cf3c28a299cc2f3cca3b78ceac396e4fcf9575e34536b26782413c
[...]
/index
 ├── c38f5fb68307c6a3e3aa945d556e325dc38f5fb68307c6a3e3aa945d556e325d
 └── ca171b1b7394d90d330b265d90f506f9984043b342525f019788f97e745c71fd
/key
 └── b02de829beeb3c01a63e6b25cbd421a98fef144f03b9a02e46eff9e2ca3f0bd7
/lock
/snapshot
 └── 22a5af1bdc6e616f8a29579458c49627e01b32210d09adb288d1ecda7c5711ec

The S3 backend understands and accepts both forms, new backends are always created with the default layout for compatibility reasons.

Pack Format

All files in the repository except Key and Pack files just contain raw data, stored as IV || Ciphertext || MAC. Pack files may contain one or more Blobs of data.

A Pack’s structure is as follows:

EncryptedBlob1 || ... || EncryptedBlobN || EncryptedHeader || Header_Length

At the end of the Pack file is a header, which describes the content. The header is encrypted and authenticated. Header_Length is the length of the encrypted header encoded as a four byte integer in little-endian encoding. Placing the header at the end of a file allows writing the blobs in a continuous stream as soon as they are read during the backup phase. This reduces code complexity and avoids having to re-write a file once the pack is complete and the content and length of the header is known.

All the blobs (EncryptedBlob1, EncryptedBlobN etc.) are authenticated and encrypted independently. This enables repository reorganisation without having to touch the encrypted Blobs. In addition it also allows efficient indexing, for only the header needs to be read in order to find out which Blobs are contained in the Pack. Since the header is authenticated, authenticity of the header can be checked without having to read the complete Pack.

After decryption, a Pack’s header consists of the following elements:

Type_Blob1 || Data_Blob1 ||
[...]
Type_BlobN || Data_BlobN ||

The Blob type field is a single byte. What follows it depends on the type. The following Blob types are defined:

Type

Meaning

Data

0b00

data blob

Length(encrypted_blob) || Hash(plaintext_blob)

0b01

tree blob

Length(encrypted_blob) || Hash(plaintext_blob)

0b10

compressed data blob

Length(encrypted_blob) || Length(plaintext_blob) || Hash(plaintext_blob)

0b11

compressed tree blob

Length(encrypted_blob) || Length(plaintext_blob) || Hash(plaintext_blob)

This is enough to calculate the offsets for all the Blobs in the Pack. The length fields are encoded as four byte integers in little-endian format. In the Data column, Length(plaintext_blob) means the length of the decrypted and uncompressed data a blob consists of.

All other types are invalid, more types may be added in the future. The compressed types are only valid for repository format version 2. Data and tree blobs may be compressed with the zstandard compression algorithm.

In repository format version 1, data and tree blobs should be stored in separate pack files. In version 2, they must be stored in separate files. Compressed and non-compress blobs of the same type may be mixed in a pack file.

For reconstructing the index or parsing a pack without an index, first the last four bytes must be read in order to find the length of the header. Afterwards, the header can be read and parsed, which yields all plaintext hashes, types, offsets and lengths of all included blobs.

Unpacked Data Format

Individual files for the index, locks or snapshots are encrypted and authenticated like Data and Tree Blobs, so the outer structure is IV || Ciphertext || MAC again. In repository format version 1 the plaintext always consists of a JSON document which must either be an object or an array.

Repository format version 2 adds support for compression. The plaintext now starts with a header to indicate the encoding version to distinguish it from plain JSON and to allow for further evolution of the storage format: encoding_version || data The encoding_version field is encoded as one byte. For backwards compatibility the encoding versions ‘[’ (0x5b) and ‘{’ (0x7b) are used to mark that the whole plaintext (including the encoding version byte) should treated as JSON document.

For new data the encoding version is currently always 2. For that version data contains a JSON document compressed using the zstandard compression algorithm.

Indexing

Index files contain information about Data and Tree Blobs and the Packs they are contained in and store this information in the repository. When the local cached index is not accessible any more, the index files can be downloaded and used to reconstruct the index. The file encoding is described in the “Unpacked Data Format” section. The plaintext consists of a JSON document like the following:

{
  "supersedes": [
    "ed54ae36197f4745ebc4b54d10e0f623eaaaedd03013eb7ae90df881b7781452"
  ],
  "packs": [
    {
      "id": "73d04e6125cf3c28a299cc2f3cca3b78ceac396e4fcf9575e34536b26782413c",
      "blobs": [
        {
          "id": "3ec79977ef0cf5de7b08cd12b874cd0f62bbaf7f07f3497a5b1bbcc8cb39b1ce",
          "type": "data",
          "offset": 0,
          "length": 38,
          // no 'uncompressed_length' as blob is not compressed
        },
        {
          "id": "9ccb846e60d90d4eb915848add7aa7ea1e4bbabfc60e573db9f7bfb2789afbae",
          "type": "data",
          "offset": 38,
          "length": 112,
          "uncompressed_length": 511,
        },
        {
          "id": "d3dc577b4ffd38cc4b32122cabf8655a0223ed22edfd93b353dc0c3f2b0fdf66",
          "type": "data",
          "offset": 150,
          "length": 123,
          "uncompressed_length": 234,
        }
      ]
    }, [...]
  ]
}

This JSON document lists Packs and the blobs contained therein. In this example, the Pack 73d04e61 contains two data Blobs and one Tree blob, the plaintext hashes are listed afterwards. The length field corresponds to Length(encrypted_blob) in the pack file header. Field uncompressed_length is only present for compressed blobs and therefore is never present in version 1 of the repository format. It is set to the value of Length(blob).

The field supersedes lists the storage IDs of index files that have been replaced with the current index file. This happens when index files are repacked, for example when old snapshots are removed and Packs are recombined.

There may be an arbitrary number of index files, containing information on non-disjoint sets of Packs. The number of packs described in a single file is chosen so that the file size is kept below 8 MiB.

Keys, Encryption and MAC

All data stored by restic in the repository is encrypted with AES-256 in counter mode and authenticated using Poly1305-AES. For encrypting new data first 16 bytes are read from a cryptographically secure pseudo-random number generator as a random nonce. This is used both as the IV for counter mode and the nonce for Poly1305. This operation needs three keys: A 32 byte for AES-256 for encryption, a 16 byte AES key and a 16 byte key for Poly1305. For details see the original paper The Poly1305-AES message-authentication code by Dan Bernstein. The data is then encrypted with AES-256 and afterwards a message authentication code (MAC) is computed over the ciphertext, everything is then stored as IV || CIPHERTEXT || MAC.

The directory keys contains key files. These are simple JSON documents which contain all data that is needed to derive the repository’s master encryption and message authentication keys from a user’s password. The JSON document from the repository can be pretty-printed for example by using the Python module json (shortened to increase readability):

$ python -mjson.tool /tmp/restic-repo/keys/b02de82*
{
    "hostname": "kasimir",
    "username": "fd0"
    "kdf": "scrypt",
    "N": 65536,
    "r": 8,
    "p": 1,
    "created": "2015-01-02T18:10:13.48307196+01:00",
    "data": "tGwYeKoM0C4j4/9DFrVEmMGAldvEn/+iKC3te/QE/6ox/V4qz58FUOgMa0Bb1cIJ6asrypCx/Ti/pRXCPHLDkIJbNYd2ybC+fLhFIJVLCvkMS+trdywsUkglUbTbi+7+Ldsul5jpAj9vTZ25ajDc+4FKtWEcCWL5ICAOoTAxnPgT+Lh8ByGQBH6KbdWabqamLzTRWxePFoYuxa7yXgmj9A==",
    "salt": "uW4fEI1+IOzj7ED9mVor+yTSJFd68DGlGOeLgJELYsTU5ikhG/83/+jGd4KKAaQdSrsfzrdOhAMftTSih5Ux6w==",
}

When the repository is opened by restic, the user is prompted for the repository password. This is then used with scrypt, a key derivation function (KDF), and the supplied parameters (N, r, p and salt) to derive 64 key bytes. The first 32 bytes are used as the encryption key (for AES-256) and the last 32 bytes are used as the message authentication key (for Poly1305-AES). These last 32 bytes are divided into a 16 byte AES key k followed by 16 bytes of secret key r. The key r is then masked for use with Poly1305 (see the paper for details).

Those keys are used to authenticate and decrypt the bytes contained in the JSON field data with AES-256 and Poly1305-AES as if they were any other blob (after removing the Base64 encoding). If the password is incorrect or the key file has been tampered with, the computed MAC will not match the last 16 bytes of the data, and restic exits with an error. Otherwise, the data yields a JSON document which contains the master encryption and message authentication keys for this repository (encoded in Base64). The command restic cat masterkey can be used as follows to decrypt and pretty-print the master key:

$ restic -r /tmp/restic-repo cat masterkey
{
    "mac": {
      "k": "evFWd9wWlndL9jc501268g==",
      "r": "E9eEDnSJZgqwTOkDtOp+Dw=="
    },
    "encrypt": "UQCqa0lKZ94PygPxMRqkePTZnHRYh1k1pX2k2lM2v3Q=",
}

All data in the repository is encrypted and authenticated with these master keys. For encryption, the AES-256 algorithm in Counter mode is used. For message authentication, Poly1305-AES is used as described above.

A repository can have several different passwords, with a key file for each. This way, the password can be changed without having to re-encrypt all data.

Snapshots

A snapshot represents a directory with all files and sub-directories at a given point in time. For each backup that is made, a new snapshot is created. A snapshot is a JSON document that is stored in a file below the directory snapshots in the repository. It uses the file encoding described in the “Unpacked Data Format” section. The filename is the storage ID. This string is unique and used within restic to uniquely identify a snapshot.

The command restic cat snapshot can be used as follows to decrypt and pretty-print the contents of a snapshot file:

$ restic -r /tmp/restic-repo cat snapshot 251c2e58
enter password for repository:
{
  "time": "2015-01-02T18:10:50.895208559+01:00",
  "tree": "2da81727b6585232894cfbb8f8bdab8d1eccd3d8f7c92bc934d62e62e618ffdf",
  "paths": [
    "/tmp/testdata"
  ],
  "hostname": "kasimir",
  "username": "fd0",
  "uid": 1000,
  "gid": 100,
  "tags": [
    "NL"
  ]
}

Here it can be seen that this snapshot represents the contents of the directory /tmp/testdata. The most important field is tree. When the meta data (e.g. the tags) of a snapshot change, the snapshot needs to be re-encrypted and saved. This will change the storage ID, so in order to relate these seemingly different snapshots, a field original is introduced which contains the ID of the original snapshot, e.g. after adding the tag DE to the snapshot above it becomes:

$ restic -r /tmp/restic-repo cat snapshot 22a5af1b
enter password for repository:
{
  "time": "2015-01-02T18:10:50.895208559+01:00",
  "tree": "2da81727b6585232894cfbb8f8bdab8d1eccd3d8f7c92bc934d62e62e618ffdf",
  "paths": [
    "/tmp/testdata"
  ],
  "hostname": "kasimir",
  "username": "fd0",
  "uid": 1000,
  "gid": 100,
  "tags": [
    "NL",
    "DE"
  ],
  "original": "251c2e5841355f743f9d4ffd3260bee765acee40a6229857e32b60446991b837"
}

Once introduced, the original field is not modified when the snapshot’s meta data is changed again.

All content within a restic repository is referenced according to its SHA-256 hash. Before saving, each file is split into variable sized Blobs of data. The SHA-256 hashes of all Blobs are saved in an ordered list which then represents the content of the file.

In order to relate these plaintext hashes to the actual location within a Pack file, an index is used. If the index is not available, the header of all data Blobs can be read.

Trees and Data

A snapshot references a tree by the SHA-256 hash of the JSON string representation of its contents. Trees and data are saved in pack files in a subdirectory of the directory data.

The command restic cat blob can be used to inspect the tree referenced above (piping the output of the command to jq . so that the JSON is indented):

$ restic -r /tmp/restic-repo cat blob 2da81727b6585232894cfbb8f8bdab8d1eccd3d8f7c92bc934d62e62e618ffdf | jq .
enter password for repository:
{
  "nodes": [
    {
      "name": "testdata",
      "type": "dir",
      "mode": 493,
      "mtime": "2014-12-22T14:47:59.912418701+01:00",
      "atime": "2014-12-06T17:49:21.748468803+01:00",
      "ctime": "2014-12-22T14:47:59.912418701+01:00",
      "uid": 1000,
      "gid": 100,
      "user": "fd0",
      "inode": 409704562,
      "content": null,
      "subtree": "b26e315b0988ddcd1cee64c351d13a100fedbc9fdbb144a67d1b765ab280b4dc"
    }
  ]
}

A tree contains a list of entries (in the field nodes) which contain meta data like a name and timestamps. Note that there are some specialties of how this metadata is generated:

  • The name is quoted using strconv.Quote before being saved. This handles non-unicode names, but also changes the representation of names containing " or \.

  • The filemode saved is the mode defined by fs.FileMode masked by os.ModePerm | os.ModeType | os.ModeSetuid | os.ModeSetgid | os.ModeSticky

When the entry references a directory, the field subtree contains the plain text ID of another tree object.

When the command restic cat blob is used, the plaintext ID is needed to print a tree. The tree referenced above can be dumped as follows:

$ restic -r /tmp/restic-repo cat blob b26e315b0988ddcd1cee64c351d13a100fedbc9fdbb144a67d1b765ab280b4dc | jq .
enter password for repository:
{
  "nodes": [
    {
      "name": "testfile",
      "type": "file",
      "mode": 420,
      "mtime": "2014-12-06T17:50:23.34513538+01:00",
      "atime": "2014-12-06T17:50:23.338468713+01:00",
      "ctime": "2014-12-06T17:50:23.34513538+01:00",
      "uid": 1000,
      "gid": 100,
      "user": "fd0",
      "inode": 416863351,
      "size": 1234,
      "links": 1,
      "content": [
        "50f77b3b4291e8411a027b9f9b9e64658181cc676ce6ba9958b95f268cb1109d"
      ]
    },
    [...]
  ]
}

This tree contains a file entry. This time, the subtree field is not present and the content field contains a list with one plain text SHA-256 hash.

A symlink uses the following data structure:

$ restic -r /tmp/restic-repo cat blob 4c0a7d500bd1482ba01752e77c8d5a923304777d96b6522fae7c11e99b4e6fa6 | jq .
enter password for repository:
{
  "nodes": [
    {
      "name": "testlink",
      "type": "symlink",
      "mode": 134218239,
      "mtime": "2023-07-25T20:01:44.007465374+02:00",
      "atime": "2023-07-25T20:01:44.007465374+02:00",
      "ctime": "2023-07-25T20:01:44.007465374+02:00",
      "uid": 1000,
      "gid": 100,
      "user": "fd0",
      "inode": 33734827,
      "links": 1,
      "linktarget": "example_target",
      "content": null
    },
    [...]
  ]
}

The symlink target is stored in the field linktarget. As JSON strings can only contain valid unicode, an exception applies if the linktarget is not a valid UTF-8 string. Since restic 0.16.0, in such a case the linktarget_raw field contains a base64 encoded version of the raw linktarget. The linktarget_raw field is only set if linktarget cannot be encoded correctly.

The command restic cat blob can also be used to extract and decrypt data given a plaintext ID, e.g. for the data mentioned above:

$ restic -r /tmp/restic-repo cat blob 50f77b3b4291e8411a027b9f9b9e64658181cc676ce6ba9958b95f268cb1109d | sha256sum
enter password for repository:
50f77b3b4291e8411a027b9f9b9e64658181cc676ce6ba9958b95f268cb1109d  -

As can be seen from the output of the program sha256sum, the hash matches the plaintext hash from the map included in the tree above, so the correct data has been returned.

Locks

The restic repository structure is designed in a way that allows parallel access of multiple instance of restic and even parallel writes. However, there are some functions that work more efficient or even require exclusive access of the repository. In order to implement these functions, restic processes are required to create a lock on the repository before doing anything.

Locks come in two types: Exclusive and non-exclusive locks. At most one process can have an exclusive lock on the repository, and during that time there must not be any other locks (exclusive and non-exclusive). There may be multiple non-exclusive locks in parallel.

A lock is a file in the subdir locks whose filename is the storage ID of the contents. It is stored in the file encoding described in the “Unpacked Data Format” section and contains the following JSON structure:

{
  "time": "2015-06-27T12:18:51.759239612+02:00",
  "exclusive": false,
  "hostname": "kasimir",
  "username": "fd0",
  "pid": 13607,
  "uid": 1000,
  "gid": 100
}

The field exclusive defines the type of lock. When a new lock is to be created, restic checks all locks in the repository. When a lock is found, it is tested if the lock is stale, which is the case for locks with timestamps older than 30 minutes. If the lock was created on the same machine, even for younger locks it is tested whether the process is still alive by sending a signal to it. If that fails, restic assumes that the process is dead and considers the lock to be stale.

When a new lock is to be created and no other conflicting locks are detected, restic creates a new lock, waits, and checks if other locks appeared in the repository. Depending on the type of the other locks and the lock to be created, restic either continues or fails. If the --retry-lock option is specified, restic will retry creating the lock periodically until it succeeds or the specified timeout expires.

Read and Write Ordering

The repository format allows writing (e.g. backup) and reading (e.g. restore) to happen concurrently. As the data for each snapshot in a repository spans multiple files (snapshot, index and packs), it is necessary to follow certain rules regarding the order in which files are read and written. These ordering rules also guarantee that repository modifications always maintain a correct repository even if the client or the storage backend crashes for example due to a power cut or the (network) connection between both is interrupted.

The correct order to access data in a repository is derived from the following set of invariants that must be maintained at any time in a correct repository. Must in the following is a strict requirement and will lead to data loss if not followed. Should will require steps to fix a repository (e.g. rebuilding the index) if not followed, but should not cause data loss. existing means that the referenced data is durably stored in the repository.

  • A snapshot must only reference an existing tree blob.

  • A reachable tree blob must only reference tree and data blobs that exist (recursively). Reachable means that the tree blob is reachable starting from a snapshot.

  • An index must only reference valid blobs in existing packs.

  • All blobs referenced by a snapshot should be listed in an index.

This leads to the following recommended order to store data in a repository. First, pack files, which contain data and tree blobs, must be written. Then the indexes which reference blobs in these already written pack files. And finally the corresponding snapshots.

Note that there is no need for a specific write order of data and tree blobs during a backup as the blobs only become referenced once the corresponding snapshot is uploaded.

Reading data should follow the opposite order compared to writing. Only once a snapshot was written, it is guaranteed that all required data exists in the repository. This especially means that the list of snapshots to read should be collected before loading the repository index. The other way round can lead to a race condition where a recently written snapshot is loaded but not its accompanying index, which results in a failure to access the snapshot’s tree blob.

For removing or rewriting data from a repository the following rules must be followed, which are derived from the above invariants.

  • A client removing data must acquire an exclusive lock first to prevent conflicts with other clients.

  • A pack must be removed from the referencing index before it is deleted.

  • Rewriting a pack must write the new pack, update the index (add an updated index and delete the old one) and only then delete the old pack.

Backups and Deduplication

For creating a backup, restic scans the source directory for all files, sub-directories and other entries. The data from each file is split into variable length Blobs cut at offsets defined by a sliding window of 64 bytes. The implementation uses Rabin Fingerprints for implementing this Content Defined Chunking (CDC). An irreducible polynomial is selected at random and saved in the file config when a repository is initialized, so that watermark attacks are much harder.

Files smaller than 512 KiB are not split, Blobs are of 512 KiB to 8 MiB in size. The implementation aims for 1 MiB Blob size on average.

For modified files, only modified Blobs have to be saved in a subsequent backup. This even works if bytes are inserted or removed at arbitrary positions within the file.

Threat Model

The design goals for restic include being able to securely store backups in a location that is not completely trusted (e.g., a shared system where others can potentially access the files) or even modify or delete them in the case of the system administrator.

General assumptions:

  • The host system a backup is created on is trusted. This is the most basic requirement, and it is essential for creating trustworthy backups.

  • The user uses an authentic copy of restic.

  • The user does not share the repository password with an attacker.

  • The restic backup program is not designed to protect against attackers deleting files at the storage location. There is nothing that can be done about this. If this needs to be guaranteed, get a secure location without any access from third parties.

  • The whole repository is re-encrypted if a key is leaked. With the current key management design, it is impossible to securely revoke a leaked key without re-encrypting the whole repository.

  • Advances in cryptography attacks against the cryptographic primitives used by restic (i.e., AES-256-CTR-Poly1305-AES and SHA-256) have not occurred. Such advances could render the confidentiality or integrity protections provided by restic useless.

  • Sufficient advances in computing have not occurred to make brute-force attacks against restic’s cryptographic protections feasible.

The restic backup program guarantees the following:

  • Unencrypted content of stored files and metadata cannot be accessed without a password for the repository. Everything except the metadata included for informational purposes in the key files is encrypted and authenticated. The cache is also encrypted to prevent metadata leaks.

  • Modifications to data stored in the repository (due to bad RAM, broken harddisk, etc.) can be detected.

  • Data that has been tampered will not be decrypted.

With the aforementioned assumptions and guarantees in mind, the following are examples of things an adversary could achieve in various circumstances.

An adversary with read access to your backup storage location could:

  • Attempt a brute force password guessing attack against a copy of the repository (please use strong passwords with sufficient entropy).

  • Infer which packs probably contain trees via file access patterns.

  • Infer the size of backups by using creation timestamps of repository objects.

An adversary with network access could:

  • Attempt to DoS the server storing the backup repository or the network connection between client and server.

  • Determine from where you create your backups (i.e., the location where the requests originate).

  • Determine where you store your backups (i.e., which provider/target system).

  • Infer the size of backups by observing network traffic.

The following are examples of the implications associated with violating some of the aforementioned assumptions.

An adversary who compromises (via malware, physical access, etc.) the host system making backups could:

  • Render the entire backup process untrustworthy (e.g., intercept password, copy files, manipulate data).

  • Create snapshots (containing garbage data) which cover all modified files and wait until a trusted host has used forget often enough to remove all correct snapshots.

  • Create a garbage snapshot for every existing snapshot with a slightly different timestamp and wait until certain forget configurations have been run, thereby removing all correct snapshots at once.

An adversary with write access to your files at the storage location could:

  • Delete or manipulate your backups, thereby impairing your ability to restore files from the compromised storage location.

  • Determine which files belong to what snapshot (e.g., based on the timestamps of the stored files). When only these files are deleted, the particular snapshot vanishes and all snapshots depending on data that has been added in the snapshot cannot be restored completely. Restic is not designed to detect this attack.

An adversary who compromises a host system with append-only (read+write allowed, delete+overwrite denied) access to the backup repository could:

  • Capture the password and decrypt backups from the past and in the future (see the “leaked key” example below for related information).

  • Render new backups untrustworthy after the host has been compromised (due to having complete control over new backups). An attacker cannot delete or manipulate old backups. As such, restoring old snapshots created before a host compromise remains possible.

  • Potentially manipulate the use of the forget command into deleting all legitimate snapshots, keeping only bogus snapshots added by the attacker. Ransomware might try this in order to leave only one option to get your data back: paying the ransom. For safe use of forget, please see the corresponding documentation on removing backup snapshots and append-only mode.

An adversary who has a leaked (decrypted) key for a repository could:

  • Decrypt existing and future backup data. If multiple hosts backup into the same repository, an attacker will get access to the backup data of every host. Note that since the local encryption key gives access to the master key, a password change will not prevent this. Changing the master key can currently only be done using the copy command, which moves the data into a new repository with a new master key, or by making a completely new repository and new backup.

Changes

Repository Version 2
  • Support compression for blobs (data/tree) and index / lock / snapshot files

Local Cache

In order to speed up certain operations, restic manages a local cache of data. The location of the cache directory depends on the operating system and the environment; see Caching.

Each repository has its own cache sub-directory, consisting of the repository ID which is chosen at init. All cache directories for different repositories are independent of each other.

Snapshots, Data and Indexes

Snapshot, Data and Index files are cached in the sub-directories snapshots, data and index, as read from the repository.

Expiry

Whenever a cache directory for a repository is used, that directory’s modification timestamp is updated to the current time. By looking at the modification timestamps of the repository cache directories it is easy to decide which directories are old and haven’t been used in a long time. Those are probably stale and can be removed.

REST Backend

Restic can interact with HTTP Backend that respects the following REST API.

The following values are valid for {type}:

  • data

  • keys

  • locks

  • snapshots

  • index

  • config

The API version is selected via the Accept HTTP header in the request. The following values are defined:

  • application/vnd.x.restic.rest.v1 or empty: Select API version 1

  • application/vnd.x.restic.rest.v2: Select API version 2

The server will respond with the value of the highest version it supports in the Content-Type HTTP response header for the HTTP requests which should return JSON. Any different value for this header means API version 1.

The placeholder {path} in this document is a path to the repository, so that multiple different repositories can be accessed. The default path is /. The path must end with a slash.

POST {path}?create=true

This request is used to initially create a new repository. The server responds with “200 OK” if the repository structure was created successfully or already exists, otherwise an error is returned.

DELETE {path}

Deletes the repository on the server side. The server responds with “200 OK” if the repository was successfully removed. If this function is not implemented the server returns “501 Not Implemented”, if this it is denied by the server it returns “403 Forbidden”.

HEAD {path}/config

Returns “200 OK” if the repository has a configuration, an HTTP error otherwise.

GET {path}/config

Returns the content of the configuration file if the repository has a configuration, an HTTP error otherwise.

Response format: binary/octet-stream

POST {path}/config

Returns “200 OK” if the configuration of the request body has been saved, an HTTP error otherwise.

GET {path}/{type}/

API version 1

Returns a JSON array containing the names of all the blobs stored for a given type, example:

[
  "245bc4c430d393f74fbe7b13325e30dbde9fb0745e50caad57c446c93d20096b",
  "85b420239efa1132c41cea0065452a40ebc20c6f8e0b132a5b2f5848360973ec",
  "8e2006bb5931a520f3c7009fe278d1ebb87eb72c3ff92a50c30e90f1b8cf3e60",
  "e75c8c407ea31ba399ab4109f28dd18c4c68303d8d86cc275432820c42ce3649"
]
API version 2

Returns a JSON array containing an object for each file of the given type. The objects have two keys: name for the file name, and size for the size in bytes.

[
  {
    "name": "245bc4c430d393f74fbe7b13325e30dbde9fb0745e50caad57c446c93d20096b",
    "size": 2341058
  },
  {
    "name": "85b420239efa1132c41cea0065452a40ebc20c6f8e0b132a5b2f5848360973ec",
    "size": 2908900
  },
  {
    "name": "8e2006bb5931a520f3c7009fe278d1ebb87eb72c3ff92a50c30e90f1b8cf3e60",
    "size": 3030712
  },
  {
    "name": "e75c8c407ea31ba399ab4109f28dd18c4c68303d8d86cc275432820c42ce3649",
    "size": 2804
  }
]

HEAD {path}/{type}/{name}

Returns “200 OK” if the blob with the given name and type is stored in the repository, “404 not found” otherwise. If the blob exists, the HTTP header Content-Length is set to the file size.

GET {path}/{type}/{name}

Returns the content of the blob with the given name and type if it is stored in the repository, “404 not found” otherwise.

If the request specifies a partial read with a Range header field, then the status code of the response is 206 instead of 200 and the response only contains the specified range.

Response format: binary/octet-stream

POST {path}/{type}/{name}

Saves the content of the request body as a blob with the given name and type, an HTTP error otherwise.

Request format: binary/octet-stream

DELETE {path}/{type}/{name}

Returns “200 OK” if the blob with the given name and type has been deleted from the repository, an HTTP error otherwise.

Talks

The following talks will be or have been given about restic:

FAQ

This is the list of Frequently Asked Questions for restic.

Will restic resume an interrupted backup?

Yes, restic will resume interrupted backups when they are re-run.

When backing up, restic periodically writes index files to keep a record of the uploaded data. Even if there’s no snapshot created in the end (due to the backup being interrupted), these indexes are stored in the repository for the data that has been uploaded so far. Next time restic runs, it is then able to find the uploaded data through these indexes, and thereby reference it again without having to upload it a second time. This effectively makes it continue from where it saved the last index, which should be up to a few minutes ago.

It does not matter if the backup was interrupted by the user or if it was due to unforeseen circumstances such as connectivity issues, power loss, etc. Simply re-run the backup again and restic should only upload what it needs to in order to complete the interrupted backup and create a snapshot.

Note however that during the initial backup run and any re-tries, until there has been a first snapshot created for the backup set (list of files and directories to be backed up), restic will need to re-scan the files on disk as there is no parent snapshot to compare the filesystem with to determine which files have changed. This process should however be far quicker than the uploading, and it’s normal to see restic scan the files again when re-running the backup.

restic check reports packs that aren’t referenced in any index, is my repository broken?

When restic check reports that there are pack files in the repository that are not referenced in any index, that’s (in contrast to what restic reports at the moment) not a source for concern. The output looks like this:

$ restic check
create exclusive lock for repository
load indexes
check all packs
pack 819a9a52e4f51230afa89aefbf90df37fb70996337ae57e6f7a822959206a85e: not referenced in any index
pack de299e69fb075354a3775b6b045d152387201f1cdc229c31d1caa34c3b340141: not referenced in any index
2 additional files were found in the repo, which likely contain duplicate data.
You can run `restic prune` to correct this.
check snapshots, trees and blobs
[0:00] 100.00%  16 / 16 snapshots
no errors were found

The message means that there is more data stored in the repository than strictly necessary. This is uncritical. With high probability this is duplicate data caused by an interrupted backup run or upload operation. In order to clean it up, the command restic prune can be used.

I ran a restic command but it is not working as intended, what do I do now?

If you are running a restic command and it is not working as you hoped it would, there is an easy way of checking how your shell interpreted the command you are trying to run.

Here is an example of a mistake in a backup command that results in the command not working as expected. A user wants to run the following restic backup command

$ restic backup --exclude "~/documents" ~

Important

This command contains an intentional user error described in this paragraph.

This command will result in a complete backup of the current logged in user’s home directory and it won’t exclude the folder ~/documents/ - which is not what the user wanted to achieve. The problem is how the path to ~/documents is passed to restic.

In order to spot an issue like this, you can make use of the following ruby command preceding your restic command.

$ ruby -e 'puts ARGV.inspect' restic backup --exclude "~/documents" ~
["restic", "backup", "--exclude", "~/documents", "/home/john"]

As you can see, the command outputs every argument you have passed to the shell. This is what restic sees when you run your command. The error here is that the tilde ~ in "~/documents" didn’t get expanded as it is quoted.

$ echo ~/documents
/home/john/documents

$ echo "~/documents"
~/document

$ echo "$HOME/documents"
/home/john/documents

Restic handles globbing and expansion in the following ways:

  • Globbing is only expanded for lines read via --files-from

  • Environment variables are not expanded in the file read via --files-from

  • * is expanded for paths read via --files-from

  • e.g. For backup targets given to restic as arguments on the shell, neither glob expansion nor shell variable replacement is done. If restic is called as restic backup '*' '$HOME', it will try to backup the literal file(s)/dir(s) * and $HOME

  • Double-asterisk ** only works in exclude patterns as this is a custom extension built into restic; the shell must not expand it

How can I specify encryption passwords automatically?

When you run restic backup, you need to enter the passphrase on the console. This is not very convenient for automated backups, so you can also provide the password through the --password-file option, or one of the environment variables RESTIC_PASSWORD or RESTIC_PASSWORD_FILE. A discussion is in progress over implementing unattended backups happens in #533.

Important

Be careful how you set the environment; using the env command, a system() call or using inline shell scripts (e.g. RESTIC_PASSWORD=password restic …) might expose the credentials in the process list directly and they will be readable to all users on a system. Using export in a shell script file should be safe, however, as the environment of a process is accessible only to that user. Please make sure that the permissions on the files where the password is eventually stored are safe (e.g. 0600 and owned by root).

How to prioritize restic’s IO and CPU time

If you’d like to change the IO priority of restic, run it in the following way

$ ionice -c2 -n0 ./restic -r /media/your/backup/ backup /home

This runs restic in the so-called best effort class (-c2), with the highest possible priority (-n0).

Take a look at the ionice manpage to learn about the other classes.

To change the CPU scheduling priority to a higher-than-standard value, use would run:

$ nice --10 ./restic -r /media/your/backup/ backup /home

Again, the nice manpage has more information.

You can also combine IO and CPU scheduling priority:

$ ionice -c2 nice -n19 ./restic -r /media/gour/backup/ backup /home

This example puts restic in the IO class 2 (best effort) and tells the CPU scheduling algorithm to give it the least favorable niceness (19).

The above example makes sure that the system the backup runs on is not slowed down, which is particularly useful for servers.

Creating new repository on a Synology NAS via sftp fails

For using restic with a Synology NAS via sftp, please make sure that the specified path is absolute, it must start with a slash (/).

Sometimes creating a new restic repository on a Synology NAS via sftp fails with an error similar to the following:

$ restic -r sftp:user@nas:/volume1/restic-repo init
create backend at sftp:user@nas:/volume1/restic-repo/ failed:
    mkdirAll(/volume1/restic-repo/index): unable to create directories: [...]

Although you can log into the NAS via SSH and see that the directory structure is there.

The reason for this behavior is that apparently Synology NAS expose a different directory structure via sftp, so the path that needs to be specified is different than the directory structure on the device and maybe even as exposed via other protocols.

Try removing the /volume1 prefix in your paths. If this does not work, use sftp and ls to explore the SFTP file system hierarchy on your NAS.

The following may work:

$ restic -r sftp:user@nas:/restic-repo init

Why does restic perform so poorly on Windows?

In some cases the real-time protection of antivirus software can interfere with restic’s operations. If you are experiencing bad performance you can try to temporarily disable your antivirus software to find out if it is the cause for your performance problems. If you are certain that the antivirus software is the cause for this and you want to gain maximum performance, you have to add the restic binary to an exclusions list within the antivirus software.

How do I choose a strong password?

Length is the single most important component in password strength. That doesn’t mean that other components such as complexity and entropy (or randomness) are not important to consider. A strong password includes Alphabetical, Numerical and Special characters. For example, nk3E9Rr26md6GGySyyWMrfakw8Jck4$&vVY6 would be a very strong password, if not for being in this documentation.

There are plenty of tools out there, such as OpenSSL, pwgen or KeePass that can generate a sufficiently complex, random and long password.

Restic backup command fails to find a valid file in Windows

If the name of a file in Windows contains an invalid character, Restic will not be able to read the file. To solve this issue, consider renaming the particular file.

Manual

Usage help

Usage help is available:

$ ./restic --help

restic is a backup program which allows saving multiple revisions of files and
directories in an encrypted repository stored on different backends.

Usage:
  restic [command]

Available Commands:
  backup        Create a new backup of files and/or directories
  cache         Operate on local cache directories
  cat           Print internal objects to stdout
  check         Check the repository for errors
  copy          Copy snapshots from one repository to another
  diff          Show differences between two snapshots
  dump          Print a backed-up file to stdout
  find          Find a file, a directory or restic IDs
  forget        Remove snapshots from the repository
  generate      Generate manual pages and auto-completion files (bash, fish, zsh, powershell)
  help          Help about any command
  init          Initialize a new repository
  key           Manage keys (passwords)
  list          List objects in the repository
  ls            List files in a snapshot
  migrate       Apply migrations
  mount         Mount the repository
  prune         Remove unneeded data from the repository
  recover       Recover data from the repository not referenced by snapshots
  repair        Repair the repository
  restore       Extract the data from a snapshot
  rewrite       Rewrite snapshots to exclude unwanted files
  self-update   Update the restic binary
  snapshots     List all snapshots
  stats         Scan the repository and show basic statistics
  tag           Modify tags on snapshots
  unlock        Remove locks other processes created
  version       Print version information

Flags:
      --cacert file                file to load root certificates from (default: use system certificates)
      --cache-dir directory        set the cache directory. (default: use system default cache directory)
      --cleanup-cache              auto remove old cache directories
      --compression mode           compression mode (only available for repository format version 2), one of (auto|off|max) (default: $RESTIC_COMPRESSION) (default auto)
  -h, --help                       help for restic
      --insecure-tls               skip TLS certificate verification when connecting to the repository (insecure)
      --json                       set output mode to JSON for commands that support it
      --key-hint key               key ID of key to try decrypting first (default: $RESTIC_KEY_HINT)
      --limit-download rate        limits downloads to a maximum rate in KiB/s. (default: unlimited)
      --limit-upload rate          limits uploads to a maximum rate in KiB/s. (default: unlimited)
      --no-cache                   do not use a local cache
      --no-lock                    do not lock the repository, this allows some operations on read-only repositories
  -o, --option key=value           set extended option (key=value, can be specified multiple times)
      --pack-size size             set target pack size in MiB, created pack files may be larger (default: $RESTIC_PACK_SIZE)
      --password-command command   shell command to obtain the repository password from (default: $RESTIC_PASSWORD_COMMAND)
  -p, --password-file file         file to read the repository password from (default: $RESTIC_PASSWORD_FILE)
  -q, --quiet                      do not output comprehensive progress report
  -r, --repo repository            repository to backup to or restore from (default: $RESTIC_REPOSITORY)
      --repository-file file       file to read the repository location from (default: $RESTIC_REPOSITORY_FILE)
      --retry-lock duration        retry to lock the repository if it is already locked, takes a value like 5m or 2h (default: no retries)
      --tls-client-cert file       path to a file containing PEM encoded TLS client certificate and private key
  -v, --verbose                    be verbose (specify multiple times or a level using --verbose=n, max level/times is 2)

Use "restic [command] --help" for more information about a command.

Similar to programs such as git, restic has a number of sub-commands. You can see these commands in the listing above. Each sub-command may have own command-line options, and there is a help option for each command which lists them, e.g. for the backup command:

$ ./restic backup --help

The "backup" command creates a new snapshot and saves the files and directories
given as the arguments.

EXIT STATUS
===========

Exit status is 0 if the command was successful.
Exit status is 1 if there was a fatal error (no snapshot created).
Exit status is 3 if some source data could not be read (incomplete snapshot created).

Usage:
  restic backup [flags] [FILE/DIR] ...

Flags:
  -n, --dry-run                                do not upload or write any data, just show what would be done
  -e, --exclude pattern                        exclude a pattern (can be specified multiple times)
      --exclude-caches                         excludes cache directories that are marked with a CACHEDIR.TAG file. See https://bford.info/cachedir/ for the Cache Directory Tagging Standard
      --exclude-file file                      read exclude patterns from a file (can be specified multiple times)
      --exclude-if-present filename[:header]   takes filename[:header], exclude contents of directories containing filename (except filename itself) if header of that file is as provided (can be specified multiple times)
      --exclude-larger-than size               max size of the files to be backed up (allowed suffixes: k/K, m/M, g/G, t/T)
      --files-from file                        read the files to backup from file (can be combined with file args; can be specified multiple times)
      --files-from-raw file                    read the files to backup from file (can be combined with file args; can be specified multiple times)
      --files-from-verbatim file               read the files to backup from file (can be combined with file args; can be specified multiple times)
  -f, --force                                  force re-reading the target files/directories (overrides the "parent" flag)
  -g, --group-by group                         group snapshots by host, paths and/or tags, separated by comma (disable grouping with '') (default host,paths)
  -h, --help                                   help for backup
  -H, --host hostname                          set the hostname for the snapshot manually. To prevent an expensive rescan use the "parent" flag
      --iexclude pattern                       same as --exclude pattern but ignores the casing of filenames
      --iexclude-file file                     same as --exclude-file but ignores casing of filenames in patterns
      --ignore-ctime                           ignore ctime changes when checking for modified files
      --ignore-inode                           ignore inode number and ctime changes when checking for modified files
      --no-scan                                do not run scanner to estimate size of backup
  -x, --one-file-system                        exclude other file systems, don't cross filesystem boundaries and subvolumes
      --parent snapshot                        use this parent snapshot (default: latest snapshot in the group determined by --group-by and not newer than the timestamp determined by --time)
      --read-concurrency n                     read n files concurrently (default: $RESTIC_READ_CONCURRENCY or 2)
      --stdin                                  read backup from stdin
      --stdin-filename filename                filename to use when reading from stdin (default "stdin")
      --tag tags                               add tags for the new snapshot in the format `tag[,tag,...]` (can be specified multiple times) (default [])
      --time time                              time of the backup (ex. '2012-11-01 22:08:41') (default: now)
      --use-fs-snapshot                        use filesystem snapshot where possible (currently only Windows VSS)
      --with-atime                             store the atime for all files and directories

Global Flags:
      --cacert file                file to load root certificates from (default: use system certificates)
      --cache-dir directory        set the cache directory. (default: use system default cache directory)
      --cleanup-cache              auto remove old cache directories
      --compression mode           compression mode (only available for repository format version 2), one of (auto|off|max) (default: $RESTIC_COMPRESSION) (default auto)
      --insecure-tls               skip TLS certificate verification when connecting to the repository (insecure)
      --json                       set output mode to JSON for commands that support it
      --key-hint key               key ID of key to try decrypting first (default: $RESTIC_KEY_HINT)
      --limit-download rate        limits downloads to a maximum rate in KiB/s. (default: unlimited)
      --limit-upload rate          limits uploads to a maximum rate in KiB/s. (default: unlimited)
      --no-cache                   do not use a local cache
      --no-lock                    do not lock the repository, this allows some operations on read-only repositories
  -o, --option key=value           set extended option (key=value, can be specified multiple times)
      --pack-size size             set target pack size in MiB, created pack files may be larger (default: $RESTIC_PACK_SIZE)
      --password-command command   shell command to obtain the repository password from (default: $RESTIC_PASSWORD_COMMAND)
  -p, --password-file file         file to read the repository password from (default: $RESTIC_PASSWORD_FILE)
  -q, --quiet                      do not output comprehensive progress report
  -r, --repo repository            repository to backup to or restore from (default: $RESTIC_REPOSITORY)
      --repository-file file       file to read the repository location from (default: $RESTIC_REPOSITORY_FILE)
      --retry-lock duration        retry to lock the repository if it is already locked, takes a value like 5m or 2h (default: no retries)
      --tls-client-cert file       path to a file containing PEM encoded TLS client certificate and private key
  -v, --verbose                    be verbose (specify multiple times or a level using --verbose=n, max level/times is 2)

Subcommands that support showing progress information such as backup, restore, check and prune will do so unless the quiet flag -q or --quiet is set. When running from a non-interactive console progress reporting is disabled by default to not fill your logs. For interactive and non-interactive consoles the environment variable RESTIC_PROGRESS_FPS can be used to control the frequency of progress reporting. Use for example 0.016666 to only update the progress once per minute.

Additionally, on Unix systems if restic receives a SIGUSR1 signal the current progress will be written to the standard output so you can check up on the status at will.

Setting the RESTIC_PROGRESS_FPS environment variable or sending a SIGUSR1 signal prints a status report even when –quiet was specified.

Manage tags

Managing tags on snapshots is done with the tag command. The existing set of tags can be replaced completely, tags can be added or removed. The result is directly visible in the snapshots command.

Let’s say we want to tag snapshot 590c8fc8 with the tags NL and CH and remove all other tags that may be present, the following command does that:

$ restic -r /srv/restic-repo tag --set NL --set CH 590c8fc8
create exclusive lock for repository
modified tags on 1 snapshots

Note the snapshot ID has changed, so between each change we need to look up the new ID of the snapshot. But there is an even better way - the tag command accepts a filter using the --tag option, so we can filter snapshots based on the tag we just added. This way we can add and remove tags incrementally:

$ restic -r /srv/restic-repo tag --tag NL --remove CH
create exclusive lock for repository
modified tags on 1 snapshots

$ restic -r /srv/restic-repo tag --tag NL --add UK
create exclusive lock for repository
modified tags on 1 snapshots

$ restic -r /srv/restic-repo tag --tag NL --remove NL
create exclusive lock for repository
modified tags on 1 snapshots

$ restic -r /srv/restic-repo tag --tag NL --add SOMETHING
no snapshots were modified

To operate on untagged snapshots only, specify the empty string '' as the filter value to --tag. The following command will add the tag OTHER to all untagged snapshots:

$ restic -r /srv/restic-repo tag --tag '' --add OTHER

Under the hood

Browse repository objects

Internally, a repository stores data of several different types described in the design documentation. You can list objects such as blobs, packs, index, snapshots, keys or locks with the following command:

$ restic -r /srv/restic-repo list snapshots
d369ccc7d126594950bf74f0a348d5d98d9e99f3215082eb69bf02dc9b3e464c

The find command searches for a given pattern in the repository.

$ restic -r backup find test.txt
debug log file restic.log
debug enabled
enter password for repository:
found 1 matching entries in snapshot 196bc5760c909a7681647949e80e5448e276521489558525680acf1bd428af36
  -rw-r--r--   501    20      5 2015-08-26 14:09:57 +0200 CEST path/to/test.txt

The cat command allows you to display the JSON representation of the objects or their raw content.

$ restic -r /srv/restic-repo cat snapshot d369ccc7d126594950bf74f0a348d5d98d9e99f3215082eb69bf02dc9b3e464c
enter password for repository:
{
  "time": "2015-08-12T12:52:44.091448856+02:00",
  "tree": "05cec17e8d3349f402576d02576a2971fc0d9f9776ce2f441c7010849c4ff5af",
  "paths": [
    "/home/user/work"
  ],
  "hostname": "kasimir",
  "username": "username",
  "uid": 501,
  "gid": 20
}

Metadata handling

Restic saves and restores most default attributes, including extended attributes like ACLs. Information about holes in a sparse file is not stored explicitly, that is during a backup the zero bytes in a hole are deduplicated and compressed like any other data backed up. Instead, the restore command optionally creates holes in files by detecting and replacing long runs of zeros, in filesystems that support sparse files.

The following metadata is handled by restic:

  • Name

  • Type

  • Mode

  • ModTime

  • AccessTime

  • ChangeTime

  • UID

  • GID

  • User

  • Group

  • Inode

  • Size

  • Links

  • LinkTarget

  • Device

  • Content

  • Subtree

  • ExtendedAttributes

Getting information about repository data

Use the stats command to count up stats about the data in the repository. There are different counting modes available using the --mode flag, depending on what you want to calculate. The default is the restore size, or the size required to restore the files:

  • restore-size (default) counts the size of the restored files.

  • files-by-contents counts the total size of unique files as given by their contents. This can be useful since a file is considered unique only if it has unique contents. Keep in mind that a small change to a large file (even when the file name/path hasn’t changed) will cause them to look like different files, thus essentially causing the whole size of the file to be counted twice.

  • raw-data counts the size of the blobs in the repository, regardless of how many files reference them. This tells you how much restic has reduced all your original data down to (either for a single snapshot or across all your backups), and compared to the size given by the restore-size mode, can tell you how much deduplication is helping you.

  • blobs-per-file is kind of a mix between files-by-contents and raw-data modes; it is useful for knowing how much value your backup is providing you in terms of unique data stored by file. Like files-by-contents, it is resilient to file renames/moves. Unlike files-by-contents, it does not balloon to high values when large files have small edits, as long as the file path stayed the same. Unlike raw-data, this mode DOES consider how many files point to each blob such that the more files a blob is referenced by, the more it counts toward the size.

For example, to calculate how much space would be required to restore the latest snapshot (from any host that made it):

$ restic stats latest
password is correct
Total File Count:   10538
      Total Size:   37.824 GiB

If multiple hosts are backing up to the repository, the latest snapshot may not be the one you want. You can specify the latest snapshot from only a specific host by using the --host flag:

$ restic stats --host myserver latest
password is correct
Total File Count:   21766
      Total Size:   481.783 GiB

There we see that it would take 482 GiB of disk space to restore the latest snapshot from “myserver”.

In case you have multiple backups running from the same host so can also use --tag and --path to be more specific about which snapshots you are looking for.

But how much space does that snapshot take on disk? In other words, how much has restic’s deduplication helped? We can check:

$ restic stats --host myserver --mode raw-data latest
password is correct
Total Blob Count:   340847
      Total Size:   458.663 GiB

Comparing this size to the previous command, we see that restic has saved about 23 GiB of space with deduplication.

Which mode you use depends on your exact use case. Some modes are more useful across all snapshots, while others make more sense on just a single snapshot, depending on what you’re trying to calculate.

Scripting

Restic supports the output of some commands in JSON format, the JSON data can then be processed by other programs (e.g. jq). The following example lists all snapshots as JSON and uses jq to pretty-print the result:

$ restic -r /srv/restic-repo snapshots --json | jq .
[
  {
    "time": "2017-03-11T09:57:43.26630619+01:00",
    "tree": "bf25241679533df554fc0fd0ae6dbb9dcf1859a13f2bc9dd4543c354eff6c464",
    "paths": [
      "/home/work/doc"
    ],
    "hostname": "kasimir",
    "username": "fd0",
    "uid": 1000,
    "gid": 100,
    "id": "bbeed6d28159aa384d1ccc6fa0b540644b1b9599b162d2972acda86b1b80f89e"
  },
  {
    "time": "2017-03-11T09:58:57.541446938+01:00",
    "tree": "7f8c95d3420baaac28dc51609796ae0e0ecfb4862b609a9f38ffaf7ae2d758da",
    "paths": [
      "/home/user/shared"
    ],
    "hostname": "kasimir",
    "username": "fd0",
    "uid": 1000,
    "gid": 100,
    "id": "b157d91c16f0ba56801ece3a708dfc53791fe2a97e827090d6ed9a69a6ebdca0"
  }
]

Temporary files

During some operations (e.g. backup and prune) restic uses temporary files to store data. These files will, by default, be saved to the system’s temporary directory, on Linux this is usually located in /tmp/. The environment variable TMPDIR can be used to specify a different directory, e.g. to use the directory /var/tmp/restic-tmp instead of the default, set the environment variable like this:

$ export TMPDIR=/var/tmp/restic-tmp
$ restic -r /srv/restic-repo backup ~/work

Caching

Restic keeps a cache with some files from the repository on the local machine. This allows faster operations, since meta data does not need to be loaded from a remote repository. The cache is automatically created, usually in an OS-specific cache folder:

  • Linux/other: $XDG_CACHE_HOME/restic, or ~/.cache/restic if XDG_CACHE_HOME is not set

  • macOS: ~/Library/Caches/restic

  • Windows: %LOCALAPPDATA%/restic

If the relevant environment variables are not set, restic exits with an error message.

The command line parameter --cache-dir or the environment variable $RESTIC_CACHE_DIR can be used to override the default cache location. The parameter --no-cache disables the cache entirely. In this case, all data is loaded from the repository.

If a cache location is explicitly specified, then the check command will use that location to store its temporary cache. See Checking integrity and consistency for more details.

The cache is ephemeral: When a file cannot be read from the cache, it is loaded from the repository.

Within the cache directory, there’s a sub directory for each repository the cache was used with. Restic updates the timestamps of a repository directory each time it is used, so by looking at the timestamps of the sub directories of the cache directory it can decide which sub directories are old and probably not needed any more. You can either remove these directories manually, or run a restic command with the --cleanup-cache flag.

Developer Information

Reproducible Builds

This section describes how to reproduce the official released binaries for restic for version 0.10.0 and later. For restic versions down to 0.9.3 please refer to the documentation for the respective version. The binary produced depends on the following things:

  • The source code for the release

  • The exact version of the official Go compiler used to produce the binaries (running restic version will print this)

  • The architecture and operating system the Go compiler runs on (Linux, amd64)

  • The build tags (for official binaries, it’s the tag selfupdate)

  • The path where the source code is extracted to (/restic)

  • The path to the Go compiler (/usr/local/go)

  • The path to the Go workspace (GOPATH=/home/build/go)

  • Other environment variables (mostly $GOOS, $GOARCH, $CGO_ENABLED)

In addition, The compressed ZIP files for Windows depends on the modification timestamp and filename of the binary contained in it. In order to reproduce the exact same ZIP file every time, we update the timestamp of the file VERSION in the source code archive and set the timezone to Europe/Berlin.

In the following example, we’ll use the file restic-0.14.0.tar.gz and Go 1.19 to reproduce the released binaries.

  1. Determine the Go compiler version used to build the released binaries, then download and extract the Go compiler into /usr/local/go:

$ restic version
restic 0.14.0 compiled with go1.19 on linux/amd64
$ cd /usr/local
$ curl -L https://dl.google.com/go/go1.19.linux-amd64.tar.gz | tar xz
  1. Extract the restic source code into /restic

$ mkdir /restic
$ cd /restic
$ TZ=Europe/Berlin curl -L https://github.com/restic/restic/releases/download/v0.14.0/restic-0.14.0.tar.gz | tar xz --strip-components=1
  1. Build the binaries for Windows and Linux:

$ export PATH=/usr/local/go/bin:$PATH
$ export GOPATH=/home/build/go
$ go version
go version go1.19 linux/amd64

$ GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w" -tags selfupdate -o restic_linux_amd64 ./cmd/restic
$ bzip2 restic_linux_amd64

$ GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w" -tags selfupdate -o restic_0.14.0_windows_amd64.exe ./cmd/restic
$ touch --reference VERSION restic_0.14.0_windows_amd64.exe
$ TZ=Europe/Berlin zip -q -X restic_0.14.0_windows_amd64.zip restic_0.14.0_windows_amd64.exe

Building the Official Binaries

The released binaries for restic are built using a Docker container. You can find it on Docker Hub as restic/builder, the Dockerfile and instructions on how to build the container can be found in the GitHub repository

The container serves the following goals: * Have a very controlled environment which is independent from the local system * Make it easy to have the correct version of the Go compiler at the right path * Make it easy to pass in the source code to build at a well-defined path

The following steps are necessary to build the binaries:

  1. Either build the container (see the instructions in the repository’s README). Alternatively, download the container from the hub:

docker pull restic/builder
  1. Extract the source code somewhere:

tar xvzf restic-0.14.0.tar.gz
  1. Create a directory to place the resulting binaries in:

mkdir output
  1. Mount the source code and the output directory in the container and run the default command, which starts helpers/build-release-binaries/main.go:

docker run --rm \
    --volume "$PWD/restic-0.14.0:/restic" \
    --volume "$PWD/output:/output" \
    restic/builder \
    go run helpers/build-release-binaries/main.go --version 0.14.0
  1. If anything goes wrong, you can enable debug output like this:

docker run --rm \
    --volume "$PWD/restic-0.14.0:/restic" \
    --volume "$PWD/output:/output" \
    restic/builder \
    go run helpers/build-release-binaries/main.go --version 0.14.0 --verbose

Verifying the Official Binaries

To verify the official binaries, you can either build them yourself using the above instructions or use the helpers/verify-release-binaries.sh script from the restic repository. Run it as helpers/verify-release-binaries.sh restic_version go_version. The specified go compiler version must match the one used to build the official binaries. For example, for restic 0.16.2 the command would be helpers/verify-release-binaries.sh 0.16.2 1.21.3.

The script requires bash, curl, docker, git, gpg, shasum and tar.

The script first downloads all release binaries, checks the SHASUM256 file and its signature. Afterwards it checks that the tarball matches the restic git repository contents, before first reproducing the builder docker container and finally the restic binaries. As final step, the restic binary in both the docker hub images and the GitHub container registry is verified. If any step fails, then the script will issue a warning.

Prepare a New Release

Publishing a new release of restic requires many different steps. We’ve automated this in the Go program helpers/prepare-release/main.go which also includes checking that e.g. the changelog is correctly generated. The only required argument is the new version number (in Semantic Versioning format MAJOR.MINOR.PATCH):

go run helpers/prepare-release/main.go 0.14.0

Checks can be skipped on demand via flags, please see --help for details.

The build process requires docker, docker-buildx and qemu-user-static-binfmt.