Title: How I Use Restic to Back up My Home Folders to Backblaze B2
Tags: restic
My guide to using the open source backup program
[restic](https://restic.net/)
to back up your home folders to
[Backblaze B2](https://www.backblaze.com/cloud-storage)
cloud storage.
**See also:**
* [restic's documentation](https://restic.readthedocs.io/en/stable/), including
the [section on B2](https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#backblaze-b2)
* [Backblaze B2's own restic guide](https://help.backblaze.com/hc/en-us/articles/4403944998811-Quickstart-Guide-for-Restic-and-Backblaze-B2-Cloud-Storage)
Backing up a new machine for the first time
-----------------------------------------------
### 1. Create a B2 bucket
All files in B2 are contained in
[buckets](https://www.backblaze.com/b2/docs/buckets.html), which are like the
top-level folders in your B2 account. Before you can back anything up to B2 you
need to create a bucket:
[Sign in to the Backblaze website](https://secure.backblaze.com/user_signin.htm),
navigate to the [B2 Cloud Storage Buckets page](https://secure.backblaze.com/b2_buckets.htm),
and click the Create a Bucket button:
Bucket names must be unique across all of B2, including the names of other user's buckets!
Hence the random numbers at the end of the bucket name.
Make sure the bucket is set to **Private**.
You can leave B2's default encryption disabled: restic will encrypt your files for you.
One-to-one mapping between buckets and repositories
You'll see below that
I create one restic repository in each B2 bucket, at the root of the bucket.
That's why my `RESTIC_REPOSITORY` environment variable contains just a bucket
name with no path (`b2:restic-seanh-laptop-79539`).
Alternatively you could create the restic repository in a subdirectory within the bucket.
Just add a path to the end of your `RESTIC_REPOSITORY` like this: `b2:bucketname:path/to/repo`.
This would allow a single B2 bucket to contain multiple restic repositories at
different paths, and I *think* B2 application keys can be given access restricted
to subpaths of buckets so different hosts don't have access to each other's backups.
I find it simpler to create a separate bucket for each restic repo.
A B2 account is limited to 100 buckets so if you have a lot of repos you might
want to try sharing buckets.
Shared versus separate repos
When backing up multiple computers they can all back up to the same restic repo
or each computer can back up to its own separate repo.
Most of the time I create a separate B2 bucket and restic repo for each machine
because it's more secure: when machines share a restic repo they have
full read write access to each other's backups. Using separate repos also
avoids locking: machines can back up to the same repo simultaneously but
if one machine is running certain maintenance commands like `restic prune` or
`restic check` then other machines can't back up to (or run maintenance
commands on) the same repo at the same time.
My main desktop and laptop do share the same repo however. This is more storage
efficient because restic will deduplicate files across the two different hosts:
if the desktop and laptop both contain copies of the same file only one copy of
that data will actually be stored in the restic repository. In this case I
think the security risk is acceptable: these machines have a lot of files in
common so the deduplication might be significant. There's already all sorts of
shared sensitive files that I have on both of these machines anyway (my GPG and
SSH keys, log ins to my Bitwarden account, etc). Both machines use full disk
encryption. To avoid locking I [run all my maintenance commands from one host](#accessing-another-hosts-backups).
When sharing a single restic repo between multiple hosts I create a separate
application key (in Backblaze) and a separate `RESTIC_PASSWORD` (using the
[`restic key` command](https://restic.readthedocs.io/en/stable/070_encryption.html),
see `restic help key`) for each machine. This means each machine's access can
be revoked individually if that machine is compromised or is no longer using the repo.
### 2. Create an application key for the B2 bucket
Restic needs a B2
[application key](https://www.backblaze.com/b2/docs/application_keys.html)
to read and write to the bucket.
Navigate to the [Application Keys page](https://secure.backblaze.com/app_keys.htm)
and click the Add a New Application Key button:
In the Allow access to Bucket(s) dropdown select only the bucket you just created.
Type of Access must be set to Read and Write.
Copy the `applicationKey` that's shown after you click Create New Key, you'll need it later:
### 3. Install pass
We're going to use [pass](https://www.passwordstore.org/) to save encrypted
copies of the application key and other restic settings on the host machine
that's going to be backed up. On the host machine:
1. Install pass.
On Ubuntu:
#!console
$ sudo apt install pass
On macOS install [Homebrew](https://brew.sh/) and then run:
#!console
$ brew install pass
2. Create a GPG keypair for pass to encrypt things with:
#!console
$ gpg --full-generate-key
...
public and secret key created and signed.
pub rsa3072 2022-04-03 [SC]
6346C69761B1155E228D488F8C23BC2681C72FBA
uid Sean Hammond (GPG key for `pass` on seanh-laptop)
sub rsa3072 2022-04-03 [E]
Take note of the key's UID (the part beginning `6346...` in my example), you'll need it for the next step.
(You can always retrieve it with `gpg --list-secret-keys`).
3. Initialize the password store with the GPG key:
#!console
$ pass init
### 4. Insert restic settings into pass
Insert the B2 bucket name, application key ID, and application key value into pass:
```console
$ pass insert "$(hostname)"/RESTIC_REPOSITORY
$ pass insert "$(hostname)"/B2_ACCOUNT_ID
$ pass insert "$(hostname)"/B2_ACCOUNT_KEY
```
We're inserting the settings into a password store subfolder named after your
hostname so that the password store can potentially be extended to hold
settings for multiple restic repos, which we'll
be doing later.
### 5. Generate a `RESTIC_PASSWORD` and save it in Bitwarden and pass
restic also needs a `RESTIC_PASSWORD` setting which is the password it'll use
to encrypt your backups. I use the cloud password manager
[Bitwarden](https://bitwarden.com/) to back up the `RESTIC_PASSWORD`:
1. Use pass to generate a password and save it in the local password store:
#!console
$ pass generate "$(hostname)"/RESTIC_PASSWORD
2. Also copy-paste the password into Bitwarden as a backup.
pass can copy the password to the clipboard for you:
#!console
$ pass show --clip "$(hostname)"/RESTIC_PASSWORD
**Don't lose your `RESTIC_PASSWORD`!**
`RESTIC_PASSWORD` is a restic thing, Backblaze doesn't know anything about it.
While `RESTIC_REPOSITORY`, `B2_ACCOUNT_ID` and `B2_ACCOUNT_KEY` can all be
retrieved or regenerated from the Backblaze website, `RESTIC_PASSWORD` can't
be. And since `RESTIC_PASSWORD` is what's used to encrypt your backups, if you
lose your `RESTIC_PASSWORD` you've lost your backups. This is why I save a copy of each
`RESTIC_PASSWORD` in Bitwarden as well as in pass on the host machine.
### 6. Create an env script
Create a `~/.restic` folder (`mkdir -p ~/.restic`) and create a
`~/.restic/env.bash` script to load the restic settings from pass into
environment variables:
```bash
#!/bin/bash
set -euo pipefail
RESTIC_HOST="$(hostname)"
RESTIC_REPOSITORY="$(pass "$RESTIC_HOST"/RESTIC_REPOSITORY)"
B2_ACCOUNT_ID="$(pass "$RESTIC_HOST"/B2_ACCOUNT_ID)"
B2_ACCOUNT_KEY="$(pass "$RESTIC_HOST"/B2_ACCOUNT_KEY)"
RESTIC_PASSWORD_COMMAND=pass\ "$RESTIC_HOST"/RESTIC_PASSWORD
export RESTIC_HOST RESTIC_REPOSITORY B2_ACCOUNT_ID B2_ACCOUNT_KEY RESTIC_PASSWORD_COMMAND
```
RESTIC_HOST
`RESTIC_HOST` isn't an environment variable recognised by restic itself. We're
exporting it because we'll use it in our backup and maintenance scripts later
on.
fish-shell
If you use fish-shell you might also want to create a `~/.restic/env.fish`
script that you can `source` from fish:
```fish
#!/usr/bin/fish
set -x RESTIC_HOST (hostname)
set -x RESTIC_REPOSITORY (pass $RESTIC_HOST/RESTIC_REPOSITORY)
set -x B2_ACCOUNT_ID (pass $RESTIC_HOST/B2_ACCOUNT_ID)
set -x B2_ACCOUNT_KEY (pass $RESTIC_HOST/B2_ACCOUNT_KEY)
set -x RESTIC_PASSWORD_COMMAND pass $RESTIC_HOST/RESTIC_PASSWORD
```
### 7. Source the env script
Now you can `source` the `env.bash` script from a bash shell and any subsequent
restic commands in that shell session will use your B2 bucket, application key,
and restic password without having to pass them as options to each command.
**The rest of the commands in this post will assume you've sourced the `env.bash` script**,
so do that now:
```console
$ source ~/.restic/env.bash
```
The script can also be sourced from other bash scripts, which we'll do below.
### 8. Install restic
To install restic on Ubuntu:
```console
$ sudo apt install restic
$ sudo restic self-update
$ # Install bash autocompletions for restic subcommands and options.
$ mkdir -p ~/.local/share/bash-completion
$ restic generate --bash-completion ~/.local/share/bash-completion/restic
```
The autocompletions enable you to press Tab after typing `restic` to
autocomplete restic subcommands, or press Tab after `--` to
autocomplete restic options.
On macOS install [Homebrew](https://brew.sh/) and then run:
```console
$ brew install restic
```
fish-shell
If you use fish-shell you might also want to install fish-shell
autocompletions:
```console
$ mkdir -p ~/.config/fish/completions
$ restic generate --fish-completion ~/.config/fish/completions/restic.fish
```
### 9. Initialize a restic repo in the B2 bucket
Initialize a restic repository (repo) in the B2 bucket:
```console
$ restic init
created restic repository 191d720a00 at b2:restic-seanh-laptop-79539
Please note that knowledge of your password is required to access
the repository. Losing your password means that your data is
irrecoverably lost.
```
If you browse your bucket in Backblaze you should see that restic has created
some housekeeping files in it:
![Files created by `restic init`.]({static}/images/restic/files.png "Files created by `restic init`.")
### 10. Create a backup script
Create a `~/.restic/backup` script that runs a `restic backup` command to back
up your home directory.
You'll want to pass some command line options to `restic backup`, consider:
`--compression max`
: Increase compression to save storage space on your backup location, at the cost of using more CPU.
`--cleanup-cache`
: Remove old cache directories after running.
`--tag `
: Add a given tag to the snapshot that the backup command creates.
Restic lets you add one or more tags (just arbitrary strings of your choosing)
to snapshots. These can be handy to filter by when listing or removing snapshots.
The example backup script below adds the tag `.restic/backup` to all snapshots it creates,
and the maintenance script further below uses this tag to identify the backup script's snapshots.
`--one-file-system`
: Excludes any other file systems that're mounted within your home directory.
`--exclude-caches`
: Excludes directories that are marked as caches by having a `CACHEDIR.TAG` file.
`--exclude `
: Excludes unwanted files from the backup.
`--exclude` can be given multiple times, and `` can be a pattern.
There are other options for excluding files as well,
see [Excluding Files](https://restic.readthedocs.io/en/stable/040_backup.html#excluding-files)
in the restic docs for details.
You might want to use Disk Usage Analyzer (`baobab`) to scan your home directory for large files
or folders that you don't want to back up.
See `restic help backup` for the rest of the available options.
Here's an example `backup` script.
This one uses your entire home directory (`$HOME`) as the path to back up,
and uses `--exclude`'s to opt-out certain paths from the backup.
Alternatively you could take an opt-in approach: instead of your entire `$HOME`
list multiple more specific paths to backup by giving multiple
positional arguments to the `restic backup` command
(there are other options for including files as well, see [Including Files](https://restic.readthedocs.io/en/stable/040_backup.html#including-files)
in the restic docs for details).
```bash
#!/bin/bash
set -euo pipefail
source ~/.restic/env.bash
restic backup \
--compression max \
--cleanup-cache \
--tag '.restic/backup' \
--group-by 'host,tags' \
--one-file-system \
--exclude-caches \
--exclude "$HOME/Downloads" \
--exclude "$HOME/Library" \
--exclude "$HOME/snap" \
--exclude "$HOME/.Trash" \
--exclude "$HOME/.bundle" \
--exclude "$HOME/.cache" \
--exclude "$HOME/.dbus" \
--exclude "$HOME/.dropbox" \
--exclude "$HOME/.dropbox-dist" \
--exclude "$HOME/.local/pipx" \
--exclude "$HOME/.local/share/Trash" \
--exclude "$HOME/.npm" \
--exclude "$HOME/.pyenv" \
--exclude "$HOME/.thumbnails" \
--exclude "$HOME/.virtualenvs" \
--exclude "node_modules" \
--exclude ".tox" \
"$HOME"
```
The --group-by 'host,tags' option
The `--group-by 'host,tags'` option in the `restic backup` command above tells restic how to find the "parent" snapshot.
If a file has already been backed up in a previous snapshot (created by a
previous `restic backup` command) restic avoids unnecessarily backing up the
same file again. To do this it has to scan the entire contents of every file
to see if they're the same, which can take a long time.
So to speed things up restic picks one previous snapshot to be the "parent"
snapshot and if a file exists in the parent snapshot that had the same path,
mtime, ctime, size and inode number as a file to be backed up then restic
assumes that's the same file and skips backing it up again *without* scanning
the entire contents of the file to make sure it's really the same.
The criteria for deciding whether two files are the same are configurable, and
the entire optimisation can also be disabled (forcing a full scan of all
files), see [File change detection](https://restic.readthedocs.io/en/stable/040_backup.html#file-change-detection)
in the restic docs for details.
The `--group-by` option tells restic how to choose the parent snapshot to use
for this optimisation. By default it's set to `host,paths` which means it'll
pick the latest snapshot with the same host and paths as the new snapshot being
created. This means that if you change the paths in your backup script from
one snapshot to the next then previous snapshots with different paths may not
be eligible to be parent snapshots and restic can end up having to rescan a lot
of files.
`--group-by 'host,tags'` tells restic to pick the latest snapshot with the same
host and tags, and since our backup script always uses `--tag '.restic/backup'`
this'll pick the latest snapshot created by the script. `host` is there in
case you use the same script to back up multiple hosts to the same repo with
the same tag: snapshots from one host will only choose previous snapshots from
that same host as parents.
### 11. Make the backup script executable
```console
$ chmod u+x ~/.restic/backup
```
### 12. Do the initial backup
Finally, to backup your home directory to B2 just run:
```console
$ ~/.restic/backup
```
This will take a long time to run. Once it's finished you'll see more files in
your B2 bucket:
![Files created by `restic backup`.]({static}/images/restic/finished-files.png "Files created by `restic backup`.")
Updating a backup
-----------------
To incrementally update a machine's backup, uploading only new or modified files,
just re-run the same backup script:
```console
$ ~/.restic/backup
```
Interrupting and resuming a backup
----------------------------------
You can interrupt a backup with Ctrl + c and later resume
it by re-running the same `~/.restic/backup` command.
See [Will restic resume an interrupted backup?](https://restic.readthedocs.io/en/stable/faq.html#will-restic-resume-an-interrupted-backup)
in restic's FAQ.
Limiting bandwidth usage
------------------------
You can limit restic's speed so it doesn't hog all your bandwidth when you're
updating a backup, just add the `--limit-download` and `--limit-upload`
options. You might not want to do this for your initial backup, but once
your first backup has completed consider adding these to your backup script:
```console
$ restic backup --limit-download 5600 --limit-upload 1800 ...
```
The values are the maximum rates in KiB/s.
Other commands like `restic check` (for verifying a backup) and `restic restore` (for restoring a backup)
also support the the `--limit-download` and `--limit-upload` options.
Backing up just one file to the same repo
-----------------------------------------
You can run a one-off command to quickly back up one or more specific files or
directories to the same repo, without doing an incremental backup of the
whole machine. Just pass the file path(s) to `restic backup`.
This while create a separate snapshot that contains only the given file(s):
```console
$ restic backup --verbose=3 foo.txt
open repository
repository d59c3d15 opened successfully, password is correct
lock repository
load index files
no parent snapshot found, will read all files
start scan on [foo.txt]
start backup on [foo.txt]
scan finished in 28.574s: 1 files, 4 B
new /foo.txt, saved in 0.004s (4 B added)
Files: 1 new, 0 changed, 0 unmodified
Dirs: 0 new, 0 changed, 0 unmodified
Data Blobs: 1 new
Tree Blobs: 1 new
Added to the repo: 384 B
processed 1 files, 4 B in 0:35
snapshot c6d56f86 saved
```
Doing a dry run with `restic backup --dry-run`
----------------------------------------------
You can always test what a `restic backup` command would do by passing the
`--dry-run` and `--verbose` arguments.
This can be good for testing whether your exclude options will do what you expect,
or checking how much data will be added to the backup before actually running it:
```console
$ restic backup --dry-run --verbose=3 foo.txt
open repository
repository d59c3d15 opened successfully, password is correct
lock repository
load index files
no parent snapshot found, will read all files
start scan on [foo.txt]
start backup on [foo.txt]
scan finished in 8.994s: 1 files, 4 B
new /foo.txt, saved in 0.003s (0 B added)
Files: 1 new, 0 changed, 0 unmodified
Dirs: 0 new, 0 changed, 0 unmodified
Data Blobs: 0 new
Tree Blobs: 1 new
Would add to the repo: 380 B
processed 1 files, 4 B in 0:09
```
Comparing two snapshots with `restic diff`
------------------------------------------
You can use `restic diff` to compare the contents of two snapshots.
Comparing a snapshot to the previous snapshot can be like a
retrospective `--dry-run`, showing you what a `restic backup` did.
First use [`restic snapshots`](#listing-snapshots-with-restic-snapshots) to get
the IDs of two snapshots that you want to compare, then pass them to `restic
diff`:
```console
$ restic diff 9eb356c4 b072e1ac
- foo.txt
+ bar.txt
M gar.txt
Files: 1 new, 1 removed, 1 changed
Dirs: 0 new, 0 removed
Others: 0 new, 0 removed
Data Blobs: 1 new, 1 removed
Tree Blobs: 1 new, 1 removed
Added: 384 B
Removed: 51.780 GiB
```
In the list of files `+` denotes a file that was added,
`-` is a file that was removed,
`M` means the file's contents were modified,
`U` means the file's metadata was changed
and `T` mean's the file's type was changed (for example it changed from a file
to a symlink).
Restoring files from your backup
--------------------------------
### Listing snapshots with `restic snapshots`
`restic snapshots` prints out a list of all the snapshots in the repo (each
completed `restic backup` command creates a new snapshot).
This is mostly useful for getting snapshot IDs to pass to commands like
`restic restore` (see below).
```console
$ restic snapshots
repository d59c3d15 opened successfully, password is correct
ID Time Host Tags Paths
--------------------------------------------------------------------
1f5cfe20 2020-04-28 09:04:47 beatsworking /home/seanh
81754433 2020-12-24 17:43:24 chamlis /home/seanh
27ca05c7 2021-04-29 18:20:00 chamlis /home/seanh
f362f638 2021-05-28 14:13:38 chamlis /home/seanh
ad4bf831 2021-06-10 18:32:55 chamlis /home/seanh
1fa5c822 2021-07-29 17:26:37 chamlis /home/seanh
50d3f0c2 2021-08-28 18:10:00 chamlis /home/seanh
2a62a5ed 2021-09-23 17:18:26 chamlis /home/seanh
9ae436fb 2021-10-28 17:01:38 chamlis /home/seanh
09f6ec02 2021-11-25 17:17:52 chamlis /home/seanh
053973eb 2021-12-25 19:43:58 chamlis /home/seanh
7939dbe7 2022-01-27 19:49:08 chamlis /home/seanh
43b899df 2022-02-26 16:55:29 chamlis /home/seanh
e377529b 2022-04-01 19:16:42 chamlis /home/seanh
9eb356c4 2022-04-01 20:37:36 beatsworking /home/seanh
22a390d4 2022-04-02 23:41:36 beatsworking /home/seanh
--------------------------------------------------------------------
16 snapshots
```
### Listing files in snapshots with `restic ls`
`restic ls` lists files within snapshots.
This is mostly useful for finding files to restore with `restic restore` (see
below).
```console
$ # List all files in the latest snapshot from any host.
$ restic ls latest
repository d59c3d15 opened successfully, password is correct
snapshot 7518e078 of [/home/seanh/foo.txt] filtered by [] at 2022-04-02 22:23:21.053983381 +0100 BST):
/foo.txt
$ # List all files in the latest snapshot from the current host.
$ restic ls --host "$(hostname)" latest
repository d59c3d15 opened successfully, password is correct
snapshot 7518e078 of [/home/seanh/foo.txt] filtered by [] at 2022-04-02 22:23:21.053983381 +0100 BST):
/foo.txt
$ # List all files in the latest snapshot of your home directory from the
$ # current host.
$ restic ls --host "$(hostname)" --path "$HOME" latest
repository d59c3d15 opened successfully, password is correct
snapshot 7518e078 of [/home/seanh/foo.txt] filtered by [] at 2022-04-02 22:23:21.053983381 +0100 BST):
/foo.txt
$ # List all files in a specific snapshot by snapshot ID.
$ restic ls 7518e078
repository d59c3d15 opened successfully, password is correct
snapshot 7518e078 of [/home/seanh/foo.txt] filtered by [] at 2022-04-02 22:23:21.053983381 +0100 BST):
/foo.txt
$ # List all files in the $HOME/Mail directory in the latest snapshot.
$ # When one or more directory arguments are given it only lists top-level files
$ # and folders in the given folder(s) unless you also give --recursive to tell
$ # it to recurse into subfolders.
$ restic ls --recursive latest $HOME/Mail
...
```
### Restoring files from a snapshot with `restic restore`
Use [`restic snapshots`](#listing-snapshots-with-restic-snapshots) to find the ID of the snapshot that you want to restore from.
(`f11c451b` in the examples below).
Then to restore the entire contents of the snapshot to a `/tmp/restored` directory:
```console
$ mkdir /tmp/restored
$ restic restore f11c451b --target /tmp/restored
```
Add `--verify` to verify the contents of the files after restoring them.
Use `--include` to restore just a single file or folder from the snapshot.
First use [`restic ls`](#listing-files-in-snapshots-with-restic-ls) to find the paths of the files you want to restore
and then run a `restore` command like:
```console
$ restic restore f11c451b --target /tmp/restore-work --include /foo.txt
```
`--include` can be given multiple times to restore multiple specific files or
folders at once and patterns can be used to restore all files that match a
pattern.
There's also `--exclude` to exclude specific or matching files from the
restore, restoring everything that _isn't_ excluded.
See [Restoring from backup](https://restic.readthedocs.io/en/stable/050_restore.html)
in the restic docs or `restic help restore` for details.
#### Restoring from the "latest" snapshot
You can use `latest` instead of a snapshot ID to restore from the most recent snapshot:
```console
$ restic restore latest --target /tmp/restored
```
`latest` on its own means the most recent snapshot **from any host** and **of any path**.
If you have multiple hosts backing up to the same repo and you want to restore
the latest snapshot from the current host use `--host "$(hostname)"` with
`latest`.
If you back up multiple paths to the same repo and you want to restore the
latest snapshot of your home directory use `--path "$HOME"` with `latest`.
Here's an example combining both:
```console
$ restic restore --host "$(hostname)" --path "$HOME" latest --target /tmp/restored
```
Maintaining your backup
-----------------------
If your backup is large the `restic prune` and `restic check` commands can
download a lot of data (potentially gigabytes) because they need to load all
your indexes and snapshots. This can take a long time and cost you a lot of
money in B2 download fees. For this reason you might not want to run `prune`
and `check` too frequently. I run the `maint` script below once a year.
### Deleting snapshots with `restic forget` and `restic prune`
You can delete old snapshots to save storage space.
To delete a specific snapshot or snapshots get the snapshot ID(s) from
[`restic snapshots`](#listing-snapshots-with-restic-snapshots) and pass them to `restic forget`. For example:
```console
$ restic forget f11c451b bea27147 f7e72151
repository d59c3d15 opened successfully, password is correct
[0:02] 100.00% 3 / 3 files deleted...
```
Just forgetting snapshots doesn't actually save any space.
You then have to run `restic prune` to find and delete the data that's no
longer used by any of the remaining snapshots:
```console
$ restic prune
repository d59c3d15 opened successfully, password is correct
loading indexes...
loading all snapshots...
finding data that is still in use for 17 snapshots
[2:37] 100.00% 17 / 17 snapshots...
searching used packs...
collecting packs for deletion and repacking
[1:01] 100.00% 181618 / 181618 packs processed...
to repack: 0 blobs / 0 B
this removes: 0 blobs / 0 B
to delete: 5 blobs / 1.986 KiB
total prune: 5 blobs / 1.986 KiB
remaining: 5877282 blobs / 862.804 GiB
unused size after prune: 16.160 GiB (1.87% of remaining size)
rebuilding index
[7:50] 100.00% 181616 / 181616 packs processed...
deleting obsolete index files
[0:17] 100.00% 120 / 120 files deleted...
removing 2 old packs
[0:00] 100.00% 2 / 2 files deleted...
done
```
Restic's docs advise you to run `restic check` (see below) after doing a `restic prune`.
To automatically select which snapshots to delete use `restic forget` with one
or more `--keep-*` options. There are lots of `--keep-*` options, like
`--keep-last n` to keep the last `n` snapshots or `--keep-hourly n` to keep only
one snapshot per hour for the last `n` hours that have snapshots.
See [Removing snapshots according to a policy](https://restic.readthedocs.io/en/stable/060_forget.html#removing-snapshots-according-to-a-policy)
in restic's docs for all the details. Any snapshots that don't match one of the
given `--keep-*` options will be deleted.
Use `--dry-run` to see which snapshots would be deleted before actually
deleting them.
Here's an example `restic forget` command using some of the `--keep-*` options:
```console
$ restic forget --dry-run \
--host "$(hostname)" \
--tag '.restic/backup' \
--group-by 'host,tags' \
--keep-last 10 \
--keep-within-hourly 1d \
--keep-within-daily 7d \
--keep-within-weekly 1m \
--keep-within-monthly 1y \
--keep-within-yearly 100y
```
This will keep the 10 most recent snapshots
and also hourly snapshots for the last day,
daily snapshots for the last week,
weekly snapshots for the last month,
monthly snapshots for the last year,
and yearly snapshots for the last century.
"Last day/week/month/year/century" are relative to the date of the latest snapshot,
not to the current date.
In addition the `--host "$(hostname)"` means that it'll only delete snapshots from the current host
and the `--tag '.restic/backup'` means it'll only delete snapshots that have the `.restic/backup` tag (which the backup script above puts on all its snapshots).
All other snapshots will be deleted (if the command is re-run without the
`--dry-run`).
Again, you'd then need to run `restic prune` to actually free up the space.
The `--group-by 'host,tags'` matches the `--group-by` value used in the `restic backup` command in the backup script above.
In the `restic forget` command `--group-by` tells restic how to group snapshots before applying the `--keep-*` options.
The default is `--group-by host,paths` meaning that if you run (for example) `restic forget --keep-last 3` it'll first group
snapshots by host and paths and then will keep the latest three snapshots in each group.
This default value is a safety feature to prevent accidentally deleting unrelated snapshots.
[Removing snapshots according to a policy](https://restic.readthedocs.io/en/stable/060_forget.html#removing-snapshots-according-to-a-policy) in the restic docs has all the details.
But the default value means that if you change the paths in your backup script
from one snapshot to the next, old snapshots with different paths might never
get deleted.
So `--group-by 'host,tags'` tells restic to consider all snapshots from the same host and with the same tags as a single group
and apply the `--keep-*` options to them all at once.
We're already limiting `restic forget` to snapshots of the current host with `--host "$(hostname)"`
and to snapshots created by our backup script with `--tag '.restic/backup'`
so we know we're not going to accidentally delete any unrelated snapshots.
### Verifying a backup with `restic check`
The `restic check` command verifies the integrity of your restic repository:
```console
$ restic check
using temporary cache in /tmp/restic-check-cache-423433534
repository d59c3d15 opened successfully, password is correct
created new cache in /tmp/restic-check-cache-423433534
create exclusive lock for repository
load indexes
check all packs
check snapshots, trees and blobs
[2:02:10] 100.00% 130 / 130 snapshots
no errors were found
```
`restic check` just checks the "structural integrity and consistency" of the
backup. If you want to check that the actual backed up files are correct you
can use `restic check --read-data` but this will download all the files in
the repository which can take a long time and add a lot of money to your
Backblaze bill. Alternatively you can use `restic check --read-data-subset=1G`
to check just a random one gigabyte subset of the data:
```console
$ restic check --read-data-subset=1G
using temporary cache in /tmp/restic-check-cache-844156038
repository d59c3d15 opened successfully, password is correct
created new cache in /tmp/restic-check-cache-844156038
create exclusive lock for repository
load indexes
check all packs
check snapshots, trees and blobs
[2:11:01] 100.00% 130 / 130 snapshots
read 1G of data packs
[0:06] 100.00% 1 / 1 packs
no errors were found
```
`restic check` with `--read-data` or `--read-data-subset` first checks the
repo's structural integrity and consistency (everything that a plain
`restic check` would do) and _then_ downloads and checks the files.
There are other ways to specify the subset to check as well,
see [Checking integrity and consistency](https://restic.readthedocs.io/en/stable/045_working_with_repos.html#checking-integrity-and-consistency)
in the restic docs.
### Create a maintenance script
To make maintaining your backup easy create a `~/.restic/maint` script that runs
`restic forget` with some `--keep-*` options, then `restic prune`, then `restic check`.
Here's an example:
```bash
#!/bin/bash
set -euo pipefail
source ~/.restic/env.bash
restic forget \
--host "$RESTIC_HOST" \
--tag '.restic/backup' \
--group-by 'host,tags' \
--keep-within-daily 7d \
--keep-within-weekly 1m \
--keep-within-monthly 1y \
--keep-within-yearly 100y
restic prune
restic check --read-data-subset=1G
```
Make the `maint` script executable:
```console
$ chmod u+x ~/.restic/maint
```
Now to weed out old snapshots and verify your backup you can just run:
```console
$ ~/.restic/maint
```
Accessing another host's backups
--------------------------------
Sometimes you might want to access one host's backups from a different host, for
example to recover a different host's files or to run maintenance
commands for another host's backups. I like to run the maintenance commands for
all my backups from my main desktop machine, it saves having to run them on
each laptop separately and saves the laptops from being occupied by long
running commands.
An easy way to do this is to create a separate `env.HOSTNAME.bash` file for
each host whose backups you want to access:
```console
$ ls ~/.restic/*.bash
/home/seanh/.restic/env.bash
/home/seanh/.restic/env.beatsworking.bash
/home/seanh/.restic/env.xenocrat.bash
```
The `env.bash` file is for this host's backups, the `env.beatsworking.bash` and
`env.xenocrat.bash` files are for `beatsworking` and `xenocrat`'s backups (the
hostnames of two laptops that I back up).
If you use fish-shell you'll want to create `env.HOSTNAME.fish` files for each host as well.
The contents of each `env.HOSTNAME.bash` file should be the same as the `env.bash`
file but hard-coding the appropriate hostname and home directory instead of
using `$(hostname)` and `$HOME`. Here's an example:
```bash
#!/bin/bash
set -euo pipefail
RESTIC_HOST=xenocrat
RESTIC_REPOSITORY="$(pass "$RESTIC_HOST"/RESTIC_REPOSITORY)"
B2_ACCOUNT_ID="$(pass "$RESTIC_HOST"/B2_ACCOUNT_ID)"
B2_ACCOUNT_KEY="$(pass "$RESTIC_HOST"/B2_ACCOUNT_KEY)"
RESTIC_PASSWORD_COMMAND=pass\ "$RESTIC_HOST"/RESTIC_PASSWORD
export RESTIC_HOST RESTIC_REPOSITORY B2_ACCOUNT_ID B2_ACCOUNT_KEY RESTIC_PASSWORD_COMMAND
```
For the `env.HOSTNAME.bash` scripts to work you'll need to add a
`RESTIC_REPOSITORY`, `B2_ACCOUNT_ID`, `B2_ACCOUNT_KEY` and `RESTIC_PASSWORD`
for each host for the password store:
```console
$ pass ls
Password Store
├── beatsworking
│ ├── B2_ACCOUNT_ID
│ ├── B2_ACCOUNT_KEY
│ ├── RESTIC_PASSWORD
│ └── RESTIC_REPOSITORY
├── chamlis
│ ├── B2_ACCOUNT_ID
│ ├── B2_ACCOUNT_KEY
│ ├── RESTIC_PASSWORD
│ └── RESTIC_REPOSITORY
└── xenocrat
├── B2_ACCOUNT_ID
├── B2_ACCOUNT_KEY
├── RESTIC_PASSWORD
└── RESTIC_REPOSITORY
```
When I do this I create a new application key in Backblaze and add a second
`RESTIC_PASSWORD` to the repo using the `restic key` command (remembering to
back up the `RESTIC_PASSWORD` in Bitwarden) rather than copying the same key
and password to two machines. This just means that each machine's key and
password can be revoked separately.
Now you can easily move between backups by just sourcing the env files and then
running restic commands:
```console
$ source ~/.restic/env.bash
$ restic check
...
$ source ~/.restic/env.beatsworking.bash
$ restic check
...
$ source ~/.restic/env.xenocrat.bash
$ restic check
...
```
I extended my annual maintenance script to source each env file in turn and run
my maintenance commands on each repo:
```bash
#!/bin/bash
set -euo pipefail
function maint {
# Print this so you can tell which backup restic's output belongs to.
echo "maint $1"
source "$1"
restic forget \
--host "$RESTIC_HOST" \
--tag '.restic/backup' \
--group-by 'host,tags' \
--keep-within-daily 7d \
--keep-within-weekly 1m \
--keep-within-monthly 1y \
--keep-within-yearly 100y
restic prune
restic check --read-data-subset=1G
}
for env_file in ~/.restic/env*.bash; do
maint "$env_file"
done
```
restic's built-in help
----------------------
restic has built-in reference documentation for all its subcommands and options.
For a list of restic's subcommands and global flags run `restic help` or
`restic --help`:
```console
$ restic help
```
For documentation of a subcommand and its options run `restic help `
or `restic --help`, for example:
```console
$ restic help backup
```
Troubleshooting
---------------
### "Please specify repository location" error
If you get this error:
Fatal: Please specify repository location (-r or --repository-file)
It probably means you forgot to source your env file. Run:
```console
$ source ~/.restic/env.bash
```
And then re-run your original command.
### "unable to create lock in backend" error
If you get this error:
unable to create lock in backend: repository is already locked exclusively by PID ...
...
lock was created at ...
storage ID ...
the `unlock` command can be used to remove stale locks
Just run the `restic unlock` command, as the error message says:
```console
$ restic unlock
```
And then re-run your original command.
Stale locks can happen if a restic command crashes or if a computer that was running
a restic command crashes or loses power etc.
TODO
----
### `restic find`
There's a `restic find` command for finding files that match a pattern,
searching across all snapshots at once which could be useful. It's poorly
documented and didn't seem to behave as I expected it to. Just grepping the
output of `restic ls` can probably get you pretty far.
### `restic mount`
There's a `restic mount` command for mounting your snapshots as a browseable FUSE filesystem.
You can restore files by just copying them normally.
```console
$ mkdir /tmp/restic
$ restic mount /tmp/restic
repository d59c3d15 opened successfully, password is correct
Now serving the repository at /tmp/restic/
Use another terminal or tool to browse the contents of this folder.
When finished, quit with Ctrl-c here or umount the mountpoint.
```
I found `restic mount` to be buggy when I tried it.
It worked once, but after that the mounted filesystem kept appearing to be empty.
### What if `restic check` finds a problem?
What should you do if `restic check` reports a problem with your backup?
I've never had this happen so I don't know how to handle it.
Will restic offer to fix the problem or suggest a solution?
Should you create a new repo and start again from scratch?