Setting up system backups using restic

To back up all my configs, time lapses, filament databases, etc, I've set up a package called restic to back up the contents of the /home/pi directory on my octoprint instances to cloud storage (a backblaze[*] b2 bucket in my case, but an AWS s3 bucket works just as well, or a NFS or Samba share mount, or just an external USB key that gets plugged into the octoprint instance would work as well.)

[*] Full disclosure, I work for backblaze, but this doesn't need to be backblaze specific.)

First, this guide assumes B2 bucket storage and a PI as the octoprint instance, but can be adapted to other storage backends and computers fairly easily. I'm assuming that you're comfortable with using the command line and can ssh into the octoprint instance.

Setting up Restic

You can always refer to the restic docs online, However, I'm going to link to the specific sections that deal with this setup.

The first decision is which version of restic you want to use. I've gone with the latest stable version:

pi@octopi-b1:~ $ restic version
restic 0.14.0 compiled with go1.19 on linux/arm

You could install whatever version your package manager has, but last time I tried, the version in the debian repos was very old (so old it didn't support b2), so I grabbed the linux/arm version from the binary releases page. Direct link here. You can download it directly onto the Pi by logging in and running:

$ wget https://github.com/restic/restic/releases/download/v0.14.0/restic_0.14.0_linux_arm.bz2

You now need to uncompress it

$ bzip2 -d restic_0.14.0_linux_arm.bz2

and move the resulting file someplace you can find it. I recommend /usr/local/bin

$ mv restic_0.14.0_linux_arm /usr/local/bin/restic
$ chmod a+rx /usr/local/bin/restic

Now see if you can run it:

$ restic version
restic 0.14.0 compiled with go1.19 on linux/arm

If you get an error about not finding it, then you might need to add /usr/local/bin to your path:

$ echo 'export PATH="${PATH}":/usr/local/bin' >> ~/.bashrc
$ source ~/.bashrc

Configuing and initializing the repository
The first thing we need to do is configure the repository (repo) directory/location where the backups will be stored. As I said before, I followed the directions for using Backblaze B2, but you can just as easily follow the directions for S3 or some other choice.

Once you have that set up, I like to start creating a place where all the restic specific configuration will live. I put it in a hidden directory in the user's home dir: /home/pi/.restic and have a script there that will contain the environment variables (environment.sh) needed for restic to find the repo.

$ mkdir -p /home/pi/.restic
$ cat > /home/pi/.restic/environment.sh << EOF
> export B2_ACCOUNT_ID=<B2 ACCOUNT ID>
> export B2_ACCOUNT_KEY=<B2 ACCOUNT KEY>
> export RESTIC_REPOSITORY=b2:<B2 BUCKET NAME>:restic
> export RESTIC_PASSWORD_FILE=/home/pi/.restic/repo.pw
> EOF

(Substitute appropriate values of course)
You'll notice that we refer to a non-existent file repo.pw which needs to contain the password used to secure the contents of your repository. I generate mine randomly using
$ gpg -a --gen-random 0 35 > /home/pi/.restic/repo.pw
But you can create your own if you like. You'll need to back up the values for the environment variables and the password file somewhere safe, or you won't be able to restore your backups when you need to.

Now let's initialize the repository:

$ source ~/.restic/environment.sh
$ restic init

This should only take a few seconds. If it doesn't, then make sure that you've properly set up your back-end storage.

Backing up your data
Now we're ready to back up the data for the first time

$ restic backup /home/pi

This may take a little while the first time if you have a lot of stuff in your home directory, but subsequent runs will only need to copy the information that has changed, and so will typically only take a few seconds.

Check that it created a snapshot of your data like so:

$ restic snapshots
repository ccff70e1 opened (repository version 2) successfully, password is correct
ID        Time                 Host            Tags        Paths
-----------------------------------------------------------------------
706f74ac  2023-01-03 18:45:01  octopi                      /home/pi
-----------------------------------------------------------------------
1 snapshot

Congratulations, you've created a backup of your data. That weird identifier at the beginning of the line (actually a hexadecimal number), is the snapshot ID, and you can look at the individual files backed up like so:

$ restic ls 706f74ac

Automating the backups
A single point-in-time snapshot of your data isn't all that useful, so it makes sense to keep making new snapshots. As I said earlier, subsequent backups only copy the data that has changed, and as such will only take up a little more space each time. I make backups of my octoprint instances every hour, and it takes up relatively little space.

I created a script to run all the necessary commands, and then automated running that script every hour using the cron facility.

First let's create a place for the script to live:

$ mkdir -p /home/pi/bin

My script looks like this:

$ nano ~/bin/backup
#!/bin/bash

source /home/pi/.restic/environment.sh

echo Starting backup at $(date)
/usr/local/bin/restic backup /home/pi
echo Finished backup at $(date)

Now we can make the script executable

$ chmod a+rx /home/pi/bin/backup

now run it

$ /home/pi/bin/backup
pi@octopi-b1:~ $ /home/pi/bin/backup
Starting backup at Tue 3 Jan 19:45:01 PST 2023
using parent snapshot 706f74ac

Files:           2 new,     1 changed, 10843 unmodified
Dirs:            0 new,     5 changed,  2225 unmodified
Added to the repository: 47.203 KiB (9.075 KiB stored)

processed 10846 files, 1.549 GiB in 0:36
snapshot 53d9a24b saved
Finished backup at Tue 3 Jan 19:45:41 PST 2023

Now that we've got a script to do the dirty work, let's run it on a schedule. I choose to use cron, which is a very old unix/linux utility, and slightly more cryptic than most tools these days, but it's very powerful.

You schedule cron to run things using the crontab command:

$ crontab -e

that will bring up an editor (nano by default) and allow you to schedule jobs. To run the backup every hour at 45 minutes past the hour, add the following line at the bottom of the file and save it out:

45 * * * * /home/pi/bin/backup >> /tmp/restic.log 2>&1

You can monitor the file /tmp/restic.log to check to see that stuff is running properly.

Cleaning out old snapshots

After a while, you'll have lots of old snapshots that you probably don't need any more

$ restic snapshots
repository ccff70e1 opened (repository version 2) successfully, password is correct
ID        Time                 Host        Tags        Paths
---------------------------------------------------------------
[....]
e2714de3  2023-01-03 15:45:01  octopi                  /home/pi 
aeff5b40  2023-01-03 16:45:01  octopi                  /home/pi
5479fd84  2023-01-03 17:45:01  octopi                  /home/pi
706f74ac  2023-01-03 18:45:01  octopi                  /home/pi
53d9a24b  2023-01-03 19:45:01  octopi                  /home/pi
---------------------------------------------------------------
170 snapshots

Now you can delete individual snapshots like so:

$ restic forget <snapshot_id>
[...]
$ restic prune

The prune command causes restic to actually free up the space in the repository that it no longer referencing files in current snapshots. However, deleting them manually really doesn't scale well.... it's too cumbersome to delete 2 dozen snapshots every day, so instead we can give it rules about what it should keep

$ restic forget --keep-last 10 --prune

This will cause it keep only the 10 most recent snapshots, and automatically prune or recover unused space.

I use a slightly more sophisticated version like so:

$ restic forget --keep-within 1d --keep-within-daily 30d --keep-within-weekly 1y --prune

this instructs it to keep every snapshot for the last day, a daily snapshot for the last 30 days, and a weekly snapshot for the last year. The output looks something like:

repository ccff70e1 opened (repository version 2) successfully, password is correct
Applying Policy: keep daily snapshots within 30d, weekly snapshots within 1y and all snapshots within 1d of the newest
keep 55 snapshots:
ID        Time                 Host        Tags        Reasons           Paths
---------------------------------------------------------------------------------
bc301182  2022-11-27 23:45:01  octopi-b1               weekly within 1y  /home/pi
d55079b7  2022-12-04 23:45:01  octopi-b1               daily within 30d  /home/pi
                                                       weekly within 1y
a2386c38  2022-12-05 23:45:01  octopi-b1               daily within 30d  /home/pi
7a69b78d  2022-12-06 23:45:01  octopi-b1               daily within 30d  /home/pi
a6d6698c  2022-12-07 23:45:01  octopi-b1               daily within 30d  /home/pi
d03754c4  2022-12-08 23:45:01  octopi-b1               daily within 30d  /home/pi
3b676494  2022-12-09 23:45:01  octopi-b1               daily within 30d  /home/pi
d777d991  2022-12-10 23:45:01  octopi-b1               daily within 30d  /home/pi
d513dada  2022-12-11 23:45:01  octopi-b1               daily within 30d  /home/pi
                                                       weekly within 1y
675287c5  2022-12-12 23:45:02  octopi-b1               daily within 30d  /home/pi
a8d8c748  2022-12-13 23:45:01  octopi-b1               daily within 30d  /home/pi
ebadcb11  2022-12-14 23:45:01  octopi-b1               daily within 30d  /home/pi
52ba5370  2022-12-15 23:45:01  octopi-b1               daily within 30d  /home/pi
31d9ef98  2022-12-16 23:45:01  octopi-b1               daily within 30d  /home/pi
d036be7f  2022-12-17 23:45:01  octopi-b1               daily within 30d  /home/pi
295fbaf4  2022-12-18 23:45:01  octopi-b1               daily within 30d  /home/pi
                                                       weekly within 1y
95c2acdc  2022-12-19 23:45:01  octopi-b1               daily within 30d  /home/pi
234b6c01  2022-12-20 23:45:01  octopi-b1               daily within 30d  /home/pi
fce0e694  2022-12-21 23:45:01  octopi-b1               daily within 30d  /home/pi
2bb6a4e9  2022-12-22 23:45:01  octopi-b1               daily within 30d  /home/pi
9ff4e50c  2022-12-23 23:45:01  octopi-b1               daily within 30d  /home/pi
0321d6e2  2022-12-24 23:45:01  octopi-b1               daily within 30d  /home/pi
3ef73526  2022-12-25 23:45:01  octopi-b1               daily within 30d  /home/pi
                                                       weekly within 1y
137be2ba  2022-12-26 23:45:01  octopi-b1               daily within 30d  /home/pi
ca913560  2022-12-27 23:45:01  octopi-b1               daily within 30d  /home/pi
f4c70b9d  2022-12-28 23:45:01  octopi-b1               daily within 30d  /home/pi
6fdcaddf  2022-12-29 23:45:01  octopi-b1               daily within 30d  /home/pi
5f47166d  2022-12-30 23:45:01  octopi-b1               daily within 30d  /home/pi
50a4a44c  2022-12-31 23:45:01  octopi-b1               daily within 30d  /home/pi
fa55d5c1  2023-01-01 23:45:02  octopi-b1               daily within 30d  /home/pi
                                                       weekly within 1y
19956be3  2023-01-02 20:45:01  octopi-b1               within 1d         /home/pi
8c6e4b75  2023-01-02 21:45:01  octopi-b1               within 1d         /home/pi
f171ade4  2023-01-02 22:45:01  octopi-b1               within 1d         /home/pi
41180c0b  2023-01-02 23:45:01  octopi-b1               within 1d         /home/pi
                                                       daily within 30d
a0f5ad2f  2023-01-03 00:45:01  octopi-b1               within 1d         /home/pi
4853c815  2023-01-03 01:45:01  octopi-b1               within 1d         /home/pi
eaeb7249  2023-01-03 02:45:01  octopi-b1               within 1d         /home/pi
dd3b2e30  2023-01-03 03:45:01  octopi-b1               within 1d         /home/pi
dcd1de77  2023-01-03 04:45:01  octopi-b1               within 1d         /home/pi
34a6d6bc  2023-01-03 05:45:01  octopi-b1               within 1d         /home/pi
dd41ff98  2023-01-03 06:45:01  octopi-b1               within 1d         /home/pi
6201e9de  2023-01-03 07:45:02  octopi-b1               within 1d         /home/pi
c23418a6  2023-01-03 08:45:01  octopi-b1               within 1d         /home/pi
f4cc119f  2023-01-03 09:45:01  octopi-b1               within 1d         /home/pi
69d9b8ab  2023-01-03 10:45:01  octopi-b1               within 1d         /home/pi
04d15b40  2023-01-03 11:45:01  octopi-b1               within 1d         /home/pi
50320b5a  2023-01-03 12:45:01  octopi-b1               within 1d         /home/pi
ebd7d6ee  2023-01-03 13:45:01  octopi-b1               within 1d         /home/pi
0d853737  2023-01-03 14:45:01  octopi-b1               within 1d         /home/pi
e2714de3  2023-01-03 15:45:01  octopi-b1               within 1d         /home/pi
aeff5b40  2023-01-03 16:45:01  octopi-b1               within 1d         /home/pi
5479fd84  2023-01-03 17:45:01  octopi-b1               within 1d         /home/pi
706f74ac  2023-01-03 18:45:01  octopi-b1               within 1d         /home/pi
53d9a24b  2023-01-03 19:45:01  octopi-b1               within 1d         /home/pi
97091f7c  2023-01-03 19:45:41  octopi-b1               within 1d         /home/pi
                                                       daily within 30d
                                                       weekly within 1y
---------------------------------------------------------------------------------
55 snapshots

remove 116 snapshots:
ID        Time                 Host        Tags        Paths
---------------------------------------------------------------
d93273eb  2022-11-30 23:45:01  octopi-b1               /home/pi
c41753be  2022-12-01 23:45:01  octopi-b1               /home/pi
85cad9f9  2022-12-02 23:45:01  octopi-b1               /home/pi
329b535b  2022-12-03 23:45:01  octopi-b1               /home/pi
f9c37f5b  2022-12-29 00:45:01  octopi-b1               /home/pi
281e2d22  2022-12-29 01:45:01  octopi-b1               /home/pi
939faf10  2022-12-29 02:45:01  octopi-b1               /home/pi
dee3b939  2022-12-29 03:45:01  octopi-b1               /home/pi
13371466  2022-12-29 04:45:01  octopi-b1               /home/pi
b6edc3aa  2022-12-29 05:45:02  octopi-b1               /home/pi
f731d67c  2022-12-29 06:45:02  octopi-b1               /home/pi
459bae7e  2022-12-29 07:45:01  octopi-b1               /home/pi
2cf68f76  2022-12-29 08:45:01  octopi-b1               /home/pi
fd83ec35  2022-12-29 09:45:01  octopi-b1               /home/pi
5c567cb5  2022-12-29 10:45:01  octopi-b1               /home/pi
d2bdbd4a  2022-12-29 11:45:01  octopi-b1               /home/pi
c0b60b1f  2022-12-29 12:45:01  octopi-b1               /home/pi
e852c82d  2022-12-29 13:45:01  octopi-b1               /home/pi
83935235  2022-12-29 14:45:01  octopi-b1               /home/pi
d4b9718a  2022-12-29 15:45:01  octopi-b1               /home/pi
35536f21  2022-12-29 16:45:01  octopi-b1               /home/pi
e6c4c82a  2022-12-29 17:45:01  octopi-b1               /home/pi
64be62c2  2022-12-29 18:45:01  octopi-b1               /home/pi
47bb29bc  2022-12-29 19:45:01  octopi-b1               /home/pi
f73e2f84  2022-12-29 20:45:01  octopi-b1               /home/pi
adff5f08  2022-12-29 21:45:02  octopi-b1               /home/pi
1f647bdc  2022-12-29 22:45:01  octopi-b1               /home/pi
f18da850  2022-12-30 00:45:01  octopi-b1               /home/pi
ee41dabb  2022-12-30 01:45:01  octopi-b1               /home/pi
063e561e  2022-12-30 02:45:01  octopi-b1               /home/pi
6c43ee77  2022-12-30 03:45:02  octopi-b1               /home/pi
5967bd00  2022-12-30 04:45:01  octopi-b1               /home/pi
5b97abc0  2022-12-30 05:45:01  octopi-b1               /home/pi
b642cf8e  2022-12-30 06:45:01  octopi-b1               /home/pi
03aad30a  2022-12-30 07:45:01  octopi-b1               /home/pi
00088559  2022-12-30 08:45:01  octopi-b1               /home/pi
868b532d  2022-12-30 09:45:01  octopi-b1               /home/pi
daf7416b  2022-12-30 10:45:01  octopi-b1               /home/pi
461339ed  2022-12-30 11:45:01  octopi-b1               /home/pi
d0abcc5b  2022-12-30 12:45:01  octopi-b1               /home/pi
febc08be  2022-12-30 13:45:01  octopi-b1               /home/pi
82337059  2022-12-30 14:45:01  octopi-b1               /home/pi
3aba444c  2022-12-30 15:45:01  octopi-b1               /home/pi
83d39387  2022-12-30 16:45:01  octopi-b1               /home/pi
7bbe4ac6  2022-12-30 17:45:01  octopi-b1               /home/pi
712dca9d  2022-12-30 18:45:01  octopi-b1               /home/pi
ddeec78c  2022-12-30 19:45:01  octopi-b1               /home/pi
7d023178  2022-12-30 20:45:01  octopi-b1               /home/pi
212be964  2022-12-30 21:45:01  octopi-b1               /home/pi
69697da7  2022-12-30 22:45:01  octopi-b1               /home/pi
6ff170e4  2022-12-31 00:45:01  octopi-b1               /home/pi
077b2bd9  2022-12-31 01:45:01  octopi-b1               /home/pi
d031315d  2022-12-31 02:45:01  octopi-b1               /home/pi
09a1c296  2022-12-31 03:45:01  octopi-b1               /home/pi
8b159d8b  2022-12-31 04:45:01  octopi-b1               /home/pi
eb004144  2022-12-31 05:45:01  octopi-b1               /home/pi
286a2b66  2022-12-31 06:45:01  octopi-b1               /home/pi
248e831b  2022-12-31 07:45:01  octopi-b1               /home/pi
c324aab2  2022-12-31 08:45:01  octopi-b1               /home/pi
380e7566  2022-12-31 09:45:01  octopi-b1               /home/pi
4ba8568d  2022-12-31 10:45:01  octopi-b1               /home/pi
91caf3be  2022-12-31 11:45:01  octopi-b1               /home/pi
213fd232  2022-12-31 12:45:01  octopi-b1               /home/pi
7a8d7de5  2022-12-31 13:45:01  octopi-b1               /home/pi
43f6da4c  2022-12-31 14:45:01  octopi-b1               /home/pi
49a9a3be  2022-12-31 15:45:01  octopi-b1               /home/pi
1af816cd  2022-12-31 16:45:01  octopi-b1               /home/pi
1e50584c  2022-12-31 17:45:01  octopi-b1               /home/pi
a53d431e  2022-12-31 18:45:01  octopi-b1               /home/pi
1b1e0795  2022-12-31 19:45:01  octopi-b1               /home/pi
7bcd3996  2022-12-31 20:45:01  octopi-b1               /home/pi
1542bd9d  2022-12-31 21:45:01  octopi-b1               /home/pi
e45f0a17  2022-12-31 22:45:01  octopi-b1               /home/pi
3ec2578f  2023-01-01 00:45:01  octopi-b1               /home/pi
c1ac6d30  2023-01-01 01:45:01  octopi-b1               /home/pi
52d6b535  2023-01-01 02:45:01  octopi-b1               /home/pi
60987619  2023-01-01 03:45:01  octopi-b1               /home/pi
b176a46a  2023-01-01 04:45:01  octopi-b1               /home/pi
08e58b8d  2023-01-01 05:45:01  octopi-b1               /home/pi
db075305  2023-01-01 06:45:01  octopi-b1               /home/pi
b077f1d1  2023-01-01 07:45:01  octopi-b1               /home/pi
7453f62d  2023-01-01 08:45:01  octopi-b1               /home/pi
42b7654e  2023-01-01 09:45:01  octopi-b1               /home/pi
a9ae67a5  2023-01-01 10:45:01  octopi-b1               /home/pi
da32f883  2023-01-01 11:45:01  octopi-b1               /home/pi
a9ebfc4c  2023-01-01 12:45:01  octopi-b1               /home/pi
c6905e1c  2023-01-01 13:45:01  octopi-b1               /home/pi
9f6a6baa  2023-01-01 14:45:01  octopi-b1               /home/pi
716c2cea  2023-01-01 15:45:01  octopi-b1               /home/pi
b5a120a1  2023-01-01 16:45:01  octopi-b1               /home/pi
ecba02bd  2023-01-01 17:45:01  octopi-b1               /home/pi
3ef2ca8c  2023-01-01 18:45:01  octopi-b1               /home/pi
f3039c58  2023-01-01 19:45:01  octopi-b1               /home/pi
99d4ea01  2023-01-01 20:45:01  octopi-b1               /home/pi
548a3d83  2023-01-01 21:45:02  octopi-b1               /home/pi
9de2959b  2023-01-01 22:45:01  octopi-b1               /home/pi
98944292  2023-01-02 00:45:01  octopi-b1               /home/pi
683135fd  2023-01-02 01:45:02  octopi-b1               /home/pi
ccd08b94  2023-01-02 02:45:02  octopi-b1               /home/pi
230c1fdf  2023-01-02 03:45:01  octopi-b1               /home/pi
5499ad9f  2023-01-02 04:45:01  octopi-b1               /home/pi
41e40927  2023-01-02 05:45:01  octopi-b1               /home/pi
899ac841  2023-01-02 06:45:01  octopi-b1               /home/pi
ae6fee5f  2023-01-02 07:45:02  octopi-b1               /home/pi
85bed7e5  2023-01-02 08:45:01  octopi-b1               /home/pi
b46e6ce0  2023-01-02 09:45:01  octopi-b1               /home/pi
3ff36509  2023-01-02 10:45:01  octopi-b1               /home/pi
deec8f55  2023-01-02 11:45:01  octopi-b1               /home/pi
990cd8e7  2023-01-02 12:45:01  octopi-b1               /home/pi
3e01debb  2023-01-02 13:45:01  octopi-b1               /home/pi
333c5db2  2023-01-02 14:45:01  octopi-b1               /home/pi
d2cf7ebe  2023-01-02 15:45:02  octopi-b1               /home/pi
94125754  2023-01-02 16:45:01  octopi-b1               /home/pi
baeecf08  2023-01-02 17:45:01  octopi-b1               /home/pi
e9b285ef  2023-01-02 18:45:01  octopi-b1               /home/pi
270f28a9  2023-01-02 19:45:01  octopi-b1               /home/pi
---------------------------------------------------------------
116 snapshots
[... prune info ...]

So in this case it kept 55 snapshots and deleted 116, and then cleaned up. I created a prune script similar to the backup script:

$ cat ~/bin/prune
#!/bin/bash

source ${HOME}/.restic/environment.sh

/usr/local/bin/restic forget --keep-within 1d --keep-within-daily 30d --keep-within-weekly 1y --prune

then created a crontab entry to prune one a day:

59 23 * * * /home/pi/bin/prune >> /tmp/restic.log 2>&1

Edited to add:
This only backs up what is in /home/pi, which won't include things like a postgress database, etc.
The plugin FilamentManager uses such a postgress database if you're trying to share a DB across multiple octoprint instances, so here's how to back that up as well:

Create a file called .pgpass in the home directory:

nano ~/.pgpass

And fill it with something like:

localhost:5432:octoprint_filamentmanager:octoprint:ThisIsNotMyRealPassword

and make it readable only by the local pi user:

chmod 600 ~/.pgpass

then add a line to the ~/bin/backup script to dump the DB into a directory that gets backed up:

pg_dump -h localhost -U octoprint octoprint_filamentmanager | gzip > /home/pi/.octoprint/data/filamentmanager/db_dump.sql.gz

And now when the backups run, they'll back up a dump of the databse as well.

Read up on how to restore from your snapshots as well.