Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b23d552f0b | ||
|
|
4142dbf20e | ||
|
|
629fd4c182 | ||
|
|
7808dd6ecc | ||
|
|
082089a203 | ||
|
|
5e51341c51 | ||
|
|
130372e641 | ||
|
|
b3c64ca2ee | ||
|
|
33a16ddb75 | ||
|
|
f1304f7db9 | ||
|
|
fad429fd34 | ||
|
|
9b7db6d999 | ||
|
|
79d13a1e64 | ||
|
|
4e8b8adff6 | ||
|
|
687111fddf | ||
|
|
f4b90c2499 | ||
|
|
341f3e79ec | ||
|
|
a4cd65db5a | ||
|
|
84bf1cfcd3 | ||
|
|
c51e5ffb03 | ||
|
|
cf978b00fe | ||
|
|
db27be517f | ||
|
|
3c9fa21b2e | ||
|
|
d8f25cdf88 | ||
|
|
16dbe699ab | ||
|
|
a011bb6dd2 | ||
|
|
15a02d4bd3 | ||
|
|
3a51ffb795 | ||
|
|
1b57069d30 | ||
|
|
5eeab95c84 | ||
|
|
3e22d8f99d | ||
|
|
9cb85a8571 | ||
|
|
7141426699 | ||
|
|
9e292218cf | ||
|
|
546845c687 | ||
|
|
828ff79e7a | ||
|
|
8932e60e4a | ||
|
|
36a90b963e | ||
|
|
5a3c8afb43 | ||
|
|
aff6211407 | ||
|
|
3e9233c0fc | ||
|
|
1d1145296d | ||
|
|
ceac35abc8 | ||
|
|
d01d2995f5 | ||
|
|
97a9475550 | ||
|
|
2e55d1cfef | ||
|
|
7a8861adc4 | ||
|
|
bf9b29575a | ||
|
|
428f4be872 | ||
|
|
99f4ba4436 | ||
|
|
ae2b122134 | ||
|
|
8e1853458c |
30
.github/workflows/linter.yml
vendored
Normal file
30
.github/workflows/linter.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Lint Code Base
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
paths:
|
||||
- '**.sh'
|
||||
- '.github/workflows/linter.yml'
|
||||
pull_request:
|
||||
branches: master
|
||||
paths:
|
||||
- '**.sh'
|
||||
- '.github/workflows/linter.yml'
|
||||
jobs:
|
||||
build:
|
||||
name: Lint Code Base
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# Full git history is needed to get a proper list of changed files within `super-linter`
|
||||
fetch-depth: 0
|
||||
- name: Lint Code Base
|
||||
uses: github/super-linter@v4
|
||||
env:
|
||||
VALIDATE_ALL_CODEBASE: true
|
||||
VALIDATE_BASH: true
|
||||
DEFAULT_BRANCH: master
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -1,2 +1,10 @@
|
||||
/etc/restic/b2_pw.txt
|
||||
/etc/restic/b2_env.sh
|
||||
# Prevent check-in of these sensitive files. Instead they are generated from the corresponding *.template file.
|
||||
etc/restic/pw.txt
|
||||
etc/restic/_global.env
|
||||
etc/restic/default.env
|
||||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
*.iml
|
||||
# VSCode
|
||||
.vscode/
|
||||
|
||||
40
CHANGELOG.md
40
CHANGELOG.md
@@ -6,6 +6,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [3.0.1] - 2022-02-01
|
||||
### Fixed
|
||||
- Environment variable assertion should allow empty values e.g. `RESTIC_BACKUP_EXTRA_ARGS`
|
||||
|
||||
## [3.0.0] - 2022-02-01
|
||||
### Added
|
||||
- Allow extra arguments to restic-backup with `$RESTIC_BACKUP_EXTRA_ARGS`.
|
||||
- Add `$RESTIC_VERBOSITY_LEVEL` for debugging.
|
||||
- Assertion on all needed environment variables in the backup and check scripts.
|
||||
- Added linter (`shellcheck(1)`) that is run on push and PRs.
|
||||
|
||||
### Changed
|
||||
- **BREAKING CHANGE** renamed
|
||||
- `/etc/restic/backup_exclude` to `/etc/restic/backup_exclude.txt`
|
||||
- `<backup-dest>/.backup_exclude` to `<backup-dest>/.backup_exclude.txt`.
|
||||
- **BREAKING CHANGE** renamed envvars for consistency
|
||||
- `BACKUP_PATHS` -> `RESTIC_BACKUP_PATHS`
|
||||
- `BACKUP_TAG` -> `RESTIC_BACKUP_TAG`
|
||||
- `RETENTION_DAYS` -> `RESTIC_RETENTION_DAYS`
|
||||
- `RETENTION_WEEKS` -> `RESTIC_RETENTION_WEEKS`
|
||||
- `RETENTION_MONTHS` -> `RESTIC_RETENTION_MONTHS`
|
||||
- `RETENTION_YEARS` -> `RESTIC_RETENTION_YEARS`
|
||||
- Align terminology used in README with the one used by B2 for credentials (keyId + applicationKey pair).
|
||||
|
||||
## [2.0.0] - 2022-02-01
|
||||
### Changed
|
||||
- **BREAKING CHANGE** [#45](https://github.com/erikw/restic-systemd-automatic-backup/pull/45): multiple backup profiles are now supported. Please backup your configuration before upgrading. The setup of configuration files are now laied out differently. See the [README.md](README.md) TL;DR setup section.
|
||||
- `restic_backup.sh` has had variables extracted to profiles instead, to allow for configuration of different backups on the same system.
|
||||
- `b2_env.sh` is split to two files `_global.env` and `default.env` (the default profile). `_global.env` will have B2 accountID and accountKey and `default.env` has backup paths, and retention.
|
||||
- `b2_pw.sh` renamed to pw.txt
|
||||
|
||||
### Fixed
|
||||
- `restic_backup.sh` now finds `.backup_exclude` files on each backup path as intended.
|
||||
|
||||
## [1.0.1] - 2021-12-03
|
||||
### Fixed
|
||||
- $(make install) now works for the *.template files ([#40](https://github.com/erikw/restic-systemd-automatic-backup/issues/40))
|
||||
|
||||
## [1.0.0] - 2021-12-02
|
||||
It's time to call this a proper major version!
|
||||
|
||||
@@ -19,4 +57,4 @@ It's time to call this a proper major version!
|
||||
- Conflicts for restic-check service
|
||||
|
||||
## [0.1.0] - 2019-07-23
|
||||
- First tagged version.
|
||||
- First tagged version to allow Arch's AUR to download a tarball archive to install.
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,7 +1,7 @@
|
||||
restic-systemd-automatic-backup - My restic backup solution using Backblaze B2 storage, systemd timers (or cron) and email notifications on failure.
|
||||
|
||||
|
||||
Copyright (c) 2018, see commit log for auhtors
|
||||
Copyright (c) 2022, Erik Westrup + see commit log for auhtors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
30
Makefile
30
Makefile
@@ -3,17 +3,18 @@
|
||||
|
||||
### Macros ###
|
||||
SRCS_SCRIPTS = $(filter-out %cron_mail, $(wildcard usr/local/sbin/*))
|
||||
SRCS_CONF = $(filter-out %template, $(wildcard etc/restic/*))
|
||||
# $(sort) remove duplicates that comes from running make install >1 times.
|
||||
SRCS_CONF = $(sort $(patsubst %.template, %, $(wildcard etc/restic/*)))
|
||||
SRCS_SYSTEMD = $(wildcard etc/systemd/system/*)
|
||||
|
||||
# Just set PREFIX in envionment, like
|
||||
# $ PREFIX=/tmp/test make
|
||||
# To change the installation root path, set the PREFIX variable in your shell's environment, like:
|
||||
# $ PREFIX=/usr/local make install
|
||||
# $ PREFIX=/tmp/test make install
|
||||
DEST_SCRIPTS = $(PREFIX)/usr/local/sbin
|
||||
DEST_CONF = $(PREFIX)/etc/restic
|
||||
DEST_SYSTEMD = $(PREFIX)/etc/systemd/system
|
||||
|
||||
INSTALLED_FILES = $(addprefix $(PREFIX)/, $(SRCS_SCRIPTS) $(SRCS_CONF) $(SRCS_SYSTEMD)) \
|
||||
$(DEST_CONF)/b2_env.sh $(DEST_CONF)/b2_pw.txt
|
||||
INSTALLED_FILES = $(addprefix $(PREFIX)/, $(SRCS_SCRIPTS) $(SRCS_CONF) $(SRCS_SYSTEMD))
|
||||
|
||||
### Targets ###
|
||||
# target: all - Default target.
|
||||
@@ -32,24 +33,25 @@ install-scripts:
|
||||
install -d $(DEST_SCRIPTS)
|
||||
install -m 0744 $(SRCS_SCRIPTS) $(DEST_SCRIPTS)
|
||||
|
||||
etc/restic/b2_env.sh:
|
||||
install -m 0600 etc/restic/b2_env.sh.template /etc/restic/b2_env.sh
|
||||
|
||||
etc/restic/b2_pw.txt:
|
||||
install -m 0600 etc/restic/b2_pw.txt.template /etc/restic/b2_pw.txt
|
||||
# Copy templates to new files with restricted permissions.
|
||||
# Why? Because the non-template files are git-ignored to prevent that someone who clones or forks this repo checks in their sensitive data like the B2 password!
|
||||
etc/restic/_global.env etc/restic/default.env etc/restic/pw.txt:
|
||||
install -m 0600 $@.template $@
|
||||
|
||||
# target: install-conf - Install restic configuration files.
|
||||
# will create these files locally only if they don't already exist
|
||||
install-conf: | etc/restic/b2_env.sh etc/restic/b2_pw.txt
|
||||
# `|` means that dependencies are order-only, i.e. only created if they don't already exist.
|
||||
install-conf: | $(SRCS_CONF)
|
||||
install -d $(DEST_CONF)
|
||||
install -m 0600 $(SRCS_CONF) $(DEST_CONF)
|
||||
install -b -m 0600 $(SRCS_CONF) $(DEST_CONF)
|
||||
$(RM) etc/restic/_global.env etc/restic/default.env etc/restic/pw.txt
|
||||
|
||||
# target: install-systemd - Install systemd timer and service files
|
||||
# target: install-systemd - Install systemd timer and service files.
|
||||
install-systemd:
|
||||
install -d $(DEST_SYSTEMD)
|
||||
install -m 0644 $(SRCS_SYSTEMD) $(DEST_SYSTEMD)
|
||||
|
||||
# target: uninstall - Uninstall files from the install targets
|
||||
# target: uninstall - Uninstall ALL files from the install targets.
|
||||
uninstall:
|
||||
@for file in $(INSTALLED_FILES); do \
|
||||
echo $(RM) $$file; \
|
||||
|
||||
183
README.md
183
README.md
@@ -2,13 +2,15 @@
|
||||
[](#)
|
||||
[](#)
|
||||
<br>
|
||||
[](#)
|
||||
[](https://github.com/erikw/restic-systemd-automatic-backup/actions/workflows/linter.yml)
|
||||
[](https://github.com/erikw/restic-systemd-automatic-backup/tags)
|
||||
[](https://aur.archlinux.org/packages/restic-systemd-automatic-backup/)
|
||||
[](https://aur.archlinux.org/packages/restic-systemd-automatic-backup/)
|
||||
[](https://github.com/erikw/restic-systemd-automatic-backup/issues)
|
||||
[](https://github.com/erikw/restic-systemd-automatic-backup/issues?q=is%3Aissue+is%3Aclosed)
|
||||
[](https://github.com/erikw/restic-systemd-automatic-backup/pulls?q=is%3Apr+is%3Aclosed)
|
||||
[](LICENSE.txt)
|
||||
[](LICENSE)
|
||||
[](https://github.com/Netflix/osstracker)
|
||||
[](https://github.com/erikw/restic-systemd-automatic-backup/tags)
|
||||
<br>
|
||||
|
||||
[](https://github.com/erikw/restic-systemd-automatic-backup/graphs/contributors) including these top contributors:
|
||||
@@ -17,83 +19,155 @@
|
||||
</a>
|
||||
|
||||
# Intro
|
||||
|
||||
[restic](https://restic.net/) is a command-line tool for making backups, the right way. Check the official website for a feature explanation. As a storage backend, I recommend [Backblaze B2](https://www.backblaze.com/b2/cloud-storage.html) as restic works well with it, and it is (at the time of writing) very affordable for the hobbyist hacker!
|
||||
[restic](https://restic.net/) is a command-line tool for making backups, the right way. Check the official website for a feature explanation. As a storage backend, I recommend [Backblaze B2](https://www.backblaze.com/b2/cloud-storage.html) as restic works well with it, and it is (at the time of writing) very affordable for the hobbyist hacker! (anecdotal: I pay for my full-systems backups each month typically < 1 USD).
|
||||
|
||||
Unfortunately restic does not come pre-configured with a way to run automated backups, say every day. However it's possible to set this up yourself using systemd/cron and some wrappers. This example also features email notifications when a backup fails to complete.
|
||||
|
||||
Here follows a step-by step tutorial on how to set it up, with my sample script and configurations that you can modify to suit your needs.
|
||||
|
||||
|
||||
Note, you can use any of the supported [storage backends](https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html). The setup should be similar but you will have to use other configuration variables to match your backend of choice.
|
||||
|
||||
# Requirements
|
||||
* `restic >=v0.9.6`
|
||||
* (recommended) `make` if you want an automated install
|
||||
* Arch: part of the `base-devel` meta package, Debian/Ubuntu: part of the `build-essential` meta package, macOS: preinstalled make works)
|
||||
|
||||
# Set up
|
||||
# TL;DR Setup
|
||||
1. Create B2 credentials as instructed [below](#1-create-backblaze-b2-account)
|
||||
1. Install config and scripts:
|
||||
```console
|
||||
$ sudo make install
|
||||
```
|
||||
☝ **Note**: `sudo` is required here, as some files are installed into system directories (`/etc/`
|
||||
and `/usr/sbin`). Have a look to the `Makefile` to know more.
|
||||
1. Fill out configuration values (edit with sudo):
|
||||
* `/etc/restic/pw.txt` - Contains the password (single line) to be used by restic to encrypt the repository files. Should be different than your B2 password!
|
||||
* `/etc/restic/_global.env` - Global environment variables.
|
||||
* `/etc/restic/default.env` - Profile specific environment variables (multiple profiles can be defined by copying to `/etc/restic/something.env`).
|
||||
* `/etc/restic/backup_exclude.txt` - List of file patterns to ignore. This will trim down your backup size and the speed of the backup a lot when done properly!
|
||||
1. Initialize remote repo as described [below](#3-initialize-remote-repo)
|
||||
1. Configure [how often](https://www.freedesktop.org/software/systemd/man/systemd.time.html#Calendar%20Events) back up should be made.
|
||||
* Edit if needed `OnCalendar` in `/etc/systemd/system/restic-check@.timer`.
|
||||
1. Enable automated backup for starting with the system (`enable` creates symlinks):
|
||||
```console
|
||||
$ sudo systemctl start restic-backup@default.timer
|
||||
$ sudo systemctl enable restic-backup@default.timer
|
||||
```
|
||||
1. And run an immediate backup if you want (if not, it will run on daily basis):
|
||||
```console
|
||||
$ sudo systemctl start restic-backup@default
|
||||
```
|
||||
1. Watch its progress with Systemd journal:
|
||||
```console
|
||||
$ journalctl -f --lines=50 -u restic-backup@default
|
||||
```
|
||||
1. Verify the backup
|
||||
```console
|
||||
$ sudo -i
|
||||
$ source /etc/restic/default.env
|
||||
$ restic snapshots
|
||||
```
|
||||
1. (optional) Define multiple profiles: just make a copy of the `default.env` and use the defined profile name in place of `default` to run backups or enable timers. Notice that the value after `@` works as a parameter.
|
||||
1. (optional) Enable the check job that verifies that the backups for the profile are all intact.
|
||||
```console
|
||||
$ sudo systemctl start restic-check@default.timer
|
||||
$ sudo systemctl enable restic-check@default.timer
|
||||
````
|
||||
1. (optional) Setup email on failure as described [here](#8-email-notification-on-failure)
|
||||
|
||||
# Step-by-step and manual setup
|
||||
This is a more detailed explanation than the TL;DR section that will give you more understanding in the setup, and maybe inspire you to develop your own setup based on this one even!
|
||||
|
||||
Tip: The steps in this section will instruct you to copy files from this repo to system directories. If you don't want to do this manually, you can use the Makefile:
|
||||
|
||||
```console
|
||||
$ git clone https://github.com/erikw/restic-systemd-automatic-backup.git
|
||||
$ cd restic-systemd-automatic-backup
|
||||
$ git clone https://github.com/erikw/restic-systemd-automatic-backup.git && cd $(basename "$_" .git)
|
||||
$ sudo make install
|
||||
````
|
||||
|
||||
## 1. Create Backblaze B2 account
|
||||
|
||||
First, see this official Backblaze [tutorial](https://help.backblaze.com/hc/en-us/articles/115002880514-How-to-configure-Backblaze-B2-with-Restic-on-Linux) on restic, and follow the instructions ("Create Backblaze account with B2 enabled") there on how to create a new B2 bucket.
|
||||
|
||||
Take note of the your account ID, application key and password for the next steps.
|
||||
|
||||
|
||||
## 2. Configure your B2 account locally
|
||||
Put these files in `/etc/restic/`:
|
||||
* `b2_env.sh`: Fill this file out with your B2 bucket settings etc. The reason for putting these in a separate file is that it can be used also for you to simply source, when you want to issue some restic commands. For example:
|
||||
Arch Linux users can install the aur package [restic-systemd-automatic-backup](https://aur.archlinux.org/packages/restic-systemd-automatic-backup/) e.g.:
|
||||
```console
|
||||
$ source /etc/restic/b2_env.sh
|
||||
$ restic snapshots # You don't have to supply all parameters like --repo, as they are now in your environment!
|
||||
$ yaourt -S restic-systemd-automatic-backup
|
||||
````
|
||||
* `b2_pw.txt`: This file should contain the restic repository password. This is a new password what soon will be used when initializing the new repository. It should be unique to this restic backup repository and is needed for restoring from it. Don't re-use your b2 login password, this should be different.
|
||||
|
||||
## 1. Create Backblaze B2 Account, Bucket and keys
|
||||
First, see this official Backblaze [tutorial](https://help.backblaze.com/hc/en-us/articles/4403944998811-Quickstart-Guide-for-Restic-and-Backblaze-B2-Cloud-Storage) on restic, and follow the instructions ("Create Backblaze account with B2 enabled") there on how to create a new B2 bucket. In general, you'd want a private bucket, without B2 encryption (restic does the encryption client side for us) and without the object lock feature.
|
||||
|
||||
For restic to be able to connect to your bucket, you want to in the B2 settings create a pair of keyID and applicationKey. It's a good idea to create a separate pair of ID and Key with for each bucket that you will use, with limited read&write access to only that bucket.
|
||||
|
||||
|
||||
## 2. Configure your B2 credentials locally
|
||||
> **Attention!** Going the manual way requires that most of the following commands are run as root.
|
||||
|
||||
Put these files in `/etc/restic/`:
|
||||
* `_global.env`: Fill this file out with your global settings including B2 keyID & applicationKey. A global exclude list is set here (explained in section below).
|
||||
* `default.env`: This is the default profile. Fill this out with bucket name, backup paths and retention policy. This file sources `_global.env` and is thus self-contained and can be sourced in the shell when you want to issue some manual restic commands. For example:
|
||||
```console
|
||||
$ source /etc/restic/default.env
|
||||
$ restic snapshots # You don't have to supply all parameters like --repo, as they are now in your environment!
|
||||
````
|
||||
* `pw.txt`: This file should contain the restic password used to encrypt the repository. This is a new password what soon will be used when initializing the new repository. It should be unique to this restic backup repository and is needed for restoring from it. Don't re-use your B2 login password, this should be different. For example you can generate a 128 character password (must all be on one line) with:
|
||||
```console
|
||||
$ openssl rand -base64 128 | tr -d '\n' > /etc/restic/pw.txt
|
||||
```
|
||||
|
||||
## 3. Initialize remote repo
|
||||
Now we must initialize the repository on the remote end:
|
||||
```console
|
||||
$ source /etc/restic/b2_env.sh
|
||||
$ sudo -i
|
||||
$ source /etc/restic/default.env
|
||||
$ restic init
|
||||
```
|
||||
|
||||
## 4. Script for doing the backup
|
||||
Put this file in `/usr/local/sbin`:
|
||||
* `restic_backup.sh`: A script that defines how to run the backup. Edit this file to respect your needs in terms of backup which paths to backup, retention (number of backups to save), etc.
|
||||
* `restic_backup.sh`: A script that defines how to run the backup. The intention is that you should not need to edit this script yourself, but be able to control everything from the `*.env` profiles.
|
||||
|
||||
Copy this file to `/etc/restic/backup_exclude` or `~/.backup_exclude`:
|
||||
* `.backup_exclude`: A list of file pattern paths to exclude from you backups, files that just occupy storage space, backup-time, network and money.
|
||||
Restic support exclude files. They list file pattern paths to exclude from you backups, files that just occupy storage space, backup-time, network and money. `restic_backup.sh` allows for a few different exclude files.
|
||||
* `/etc/restic/backup_exclude.txt` - global exclude list. You can use only this one if your setup is easy. This is set in `_global.env`. If you need a different file for another profile, you can override the envvar `RESTIC_BACKUP_EXCLUDE_FILE` in this profile.
|
||||
* `.backup_exclude.txt` per backup path. If you have e.g. an USB disk mounted at /mnt/media and this path is included in the `$RESTIC_BACKUP_PATHS`, you can place a file `/mnt/media/.backup_exclude.txt` and it will automatically picked up. The nice thing about this is that the backup paths are self-contained in terms of what they shoud exclude!
|
||||
|
||||
|
||||
## 5. Make first backup & verify
|
||||
Now see if the backup itself works, by running
|
||||
## 5. Make first backup
|
||||
Now see if the backup itself works, by running as root
|
||||
|
||||
```console
|
||||
$ sudo -i
|
||||
$ source /etc/restic/default.env
|
||||
$ /usr/local/sbin/restic_backup.sh
|
||||
$ restic snapshots
|
||||
````
|
||||
|
||||
## 6. Backup automatically; systemd service + timer
|
||||
## 6. Verify the backup
|
||||
As the `default.env` is already sourced in your root shell, you can now just list the snapshos
|
||||
```console
|
||||
$ sudo -i
|
||||
$ source /etc/restic/default.env
|
||||
$ restic snapshots
|
||||
```
|
||||
|
||||
Alternatively you can mount the restic snapshots to a directory set `/mnt/restic`
|
||||
```console
|
||||
$ restic mount /mnt/restic
|
||||
$ ls /mnt/restic
|
||||
```
|
||||
|
||||
## 7. Backup automatically; systemd service + timer
|
||||
Now we can do the modern version of a cron-job, a systemd service + timer, to run the backup every day!
|
||||
|
||||
|
||||
Put these files in `/etc/systemd/system/`:
|
||||
* `restic-backup.service`: A service that calls the backup script.
|
||||
* `restic-backup.timer`: A timer that starts the backup every day.
|
||||
|
||||
* `restic-backup@.service`: A service that calls the backup script with the specified profile. The profile is specified
|
||||
by the value after `@` when running it (see below).
|
||||
* `restic-backup@.timer`: A timer that starts the former backup every day (same thing about profile here).
|
||||
* If needed, edit this file to configure [how often](https://www.freedesktop.org/software/systemd/man/systemd.time.html#Calendar%20Events) back up should be made. See the `OnCalendar` key in the file.
|
||||
|
||||
Now simply enable the timer with:
|
||||
```console
|
||||
$ systemctl start restic-backup.timer
|
||||
$ systemctl enable restic-backup.timer
|
||||
$ systemctl start restic-backup@default.timer
|
||||
$ systemctl enable restic-backup@default.timer
|
||||
````
|
||||
|
||||
☝ **Note**: You can run it with different values instead of `default` if you use multiple profiles.
|
||||
|
||||
You can see when your next backup is scheduled to run with
|
||||
```console
|
||||
$ systemctl list-timers | grep restic
|
||||
@@ -108,20 +182,20 @@ $ systemctl status restic-backup
|
||||
or start a backup manually
|
||||
|
||||
```console
|
||||
$ systemctl start restic-backup
|
||||
$ systemctl start restic-backup@default
|
||||
```
|
||||
|
||||
You can follow the backup stdout output live as backup is running with:
|
||||
|
||||
```console
|
||||
$ journalctl -f -u restic-backup.service
|
||||
$ journalctl -f -u restic-backup@default.service
|
||||
````
|
||||
|
||||
(skip `-f` to see all backups that has run)
|
||||
|
||||
|
||||
|
||||
## 7. Email notification on failure
|
||||
## 8. Email notification on failure
|
||||
We want to be aware when the automatic backup fails, so we can fix it. Since my laptop does not run a mail server, I went for a solution to set up my laptop to be able to send emails with [postfix via my Gmail](https://easyengine.io/tutorials/linux/ubuntu-postfix-gmail-smtp/). Follow the instructions over there.
|
||||
|
||||
Put this file in `/usr/local/sbin`:
|
||||
@@ -133,10 +207,17 @@ Put this files in `/etc/systemd/system/`:
|
||||
As you maybe noticed already before, `restic-backup.service` is configured to start `status-email-user.service` on failure.
|
||||
|
||||
|
||||
## 8. Optional: automated backup checks
|
||||
## 9. Optional: automated backup checks
|
||||
Once in a while it can be good to do a health check of the remote repository, to make sure it's not getting corrupt. This can be done with `$ restic check`.
|
||||
|
||||
There are some `*-check*`-files in this git repo. Install these in the same way you installed the `*-backup*`-files.
|
||||
There is companion scripts, service and timer (`*check*`) to restic-backup.sh that checks the restic backup for errors; look in the repo in `etc/systemd/system` and `usr/local/sbin` and copy what you need over to their corresponding locations.
|
||||
|
||||
```console
|
||||
$ sudo -i
|
||||
$ systemctl start restic-check@default.timer
|
||||
$ systemctl enable restic-check@default.timer
|
||||
````
|
||||
|
||||
|
||||
|
||||
# Cron?
|
||||
@@ -152,3 +233,23 @@ There is a make target to remove all files (scripts and configs) that were insta
|
||||
```console
|
||||
$ sudo make uninstall
|
||||
```
|
||||
|
||||
# Variations
|
||||
A list of variations of this setup:
|
||||
* Using `--files-from` [#44](https://github.com/erikw/restic-systemd-automatic-backup/issues/44)
|
||||
|
||||
# Development
|
||||
To not mess up your real installation when changing the `Makefile` simply install to a `$PREFIX` like
|
||||
```console
|
||||
$ PREFIX=/tmp/restic-test make install
|
||||
```
|
||||
|
||||
# Releasing
|
||||
To make a new release:
|
||||
1.
|
||||
```console
|
||||
$ vi CHANGELOG.md && git commit -am "Update CHANGELOG.md"
|
||||
$ git tag vX.Y.Z
|
||||
$ git push && git push --tags
|
||||
```
|
||||
1. Test and update the AUR [PKGBUILD](https://aur.archlinux.org/packages/restic-systemd-automatic-backup/) if needed.
|
||||
|
||||
28
etc/restic/_global.env.template
Normal file
28
etc/restic/_global.env.template
Normal file
@@ -0,0 +1,28 @@
|
||||
# Global envionment variables
|
||||
# These variables are sourced FIRST, and any values inside of *.env files for
|
||||
# specific configurations will override if also defined there.
|
||||
|
||||
|
||||
# Official instructions on how to setup the restic variables for Backblaze B2 can be found at
|
||||
# https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#backblaze-b2
|
||||
|
||||
|
||||
# The restic repository encryption key
|
||||
export RESTIC_PASSWORD_FILE="/etc/restic/pw.txt"
|
||||
# The global restic exclude file
|
||||
export RESTIC_BACKUP_EXCLUDE_FILE="/etc/restic/backup_exclude.txt"
|
||||
|
||||
# Backblaze B2 credentials keyID & applicationKey pair.
|
||||
# Restic environment variables are documented at https://restic.readthedocs.io/en/latest/040_backup.html#environment-variables
|
||||
export B2_ACCOUNT_ID="<b2-key-id>" # TODO fill with your keyID
|
||||
export B2_ACCOUNT_KEY="<b2-application-key>" # TODO fill with your applicationKey
|
||||
|
||||
# How many network connections to set up to B2. Default is 5.
|
||||
export B2_CONNECTIONS=10
|
||||
|
||||
# Extra args to restic-backup. This is empty here and profiles can override this after sourcing this file.
|
||||
export RESTIC_BACKUP_EXTRA_ARGS=
|
||||
|
||||
# Verbosity level from 0-3. 0 means no --verbose.
|
||||
# Override this value in a profile if needed.
|
||||
export RESTIC_VERBOSITY_LEVEL=0
|
||||
@@ -1,8 +0,0 @@
|
||||
# B2 credentials.
|
||||
# Extracted settings so both systemd timers and user can just source this when want to work on my B2 backup.
|
||||
# See https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html
|
||||
|
||||
export RESTIC_REPOSITORY="b2:<b2-repo-name>"
|
||||
export RESTIC_PASSWORD_FILE="/etc/restic/b2_pw.txt"
|
||||
export B2_ACCOUNT_ID="<b2-account-id>"
|
||||
export B2_ACCOUNT_KEY="<b2-account-key>"
|
||||
@@ -1 +0,0 @@
|
||||
<b2-password>
|
||||
40
etc/restic/default.env.template
Normal file
40
etc/restic/default.env.template
Normal file
@@ -0,0 +1,40 @@
|
||||
# This is the default profile. Fill it with your desired configuration.
|
||||
# Additionally, you can create and use more profiles by copying this file.
|
||||
|
||||
# This file (and other .env files) has two purposes:
|
||||
# - being sourced by systemd timers to setup the backup before running restic_backup.sh
|
||||
# - being sourced in a user's shell to work directly with restic commands e.g.
|
||||
# $ source /etc/restic/default.env
|
||||
# $ restic snapshots
|
||||
# Thus you don't have to provide all the arguments like
|
||||
# $ restic --repo ... --password-file ...
|
||||
|
||||
source /etc/restic/_global.env
|
||||
|
||||
# Below envvar will override those in _global.env
|
||||
|
||||
export RESTIC_REPOSITORY="b2:<b2-repo-name>" # TODO fill with your repo name
|
||||
|
||||
# What to backup (paths our mountpoints) e.g. "/ /boot /home /mnt/media".
|
||||
# To backup only your home directory, set "/home/your-user"
|
||||
export RESTIC_BACKUP_PATHS="" # TODO fill conveniently with one or multiple paths
|
||||
|
||||
# Example below of how to dynamically add a path that is mounted e.g. external USB disk.
|
||||
# restic does not fail if a specified path is not mounted, but it's nicer to only add if they are available.
|
||||
#test -d /mnt/media && RESTIC_BACKUP_PATHS+=" /mnt/media"
|
||||
|
||||
# A tag to identify backup snapshots.
|
||||
export RESTIC_BACKUP_TAG=systemd.timer
|
||||
|
||||
# Retention policy - How many backups to keep.
|
||||
# See https://restic.readthedocs.io/en/stable/060_forget.html?highlight=month#removing-snapshots-according-to-a-policy
|
||||
export RESTIC_RETENTION_DAYS=14
|
||||
export RESTIC_RETENTION_WEEKS=16
|
||||
export RESTIC_RETENTION_MONTHS=18
|
||||
export RESTIC_RETENTION_YEARS=3
|
||||
|
||||
# Optional extra arguments to restic-backup.
|
||||
# Example: Add two additional exclude files to the global one in RESTIC_PASSWORD_FILE.
|
||||
#RESTIC_BACKUP_EXTRA_ARGS="--exclude-file /path/to/extra/exclude/file/a /path/to/extra/exclude/file/b"
|
||||
# Example: exclude all directories that have a .git/ directory inside it.
|
||||
#RESTIC_BACKUP_EXTRA_ARGS="--exclude-if-present .git"
|
||||
1
etc/restic/pw.txt.template
Normal file
1
etc/restic/pw.txt.template
Normal file
@@ -0,0 +1 @@
|
||||
<restic-encryption-password>
|
||||
@@ -1,11 +0,0 @@
|
||||
[Unit]
|
||||
Description=Backup with restic to Backblaze B2
|
||||
OnFailure=status-email-user@%n.service
|
||||
Requires=nm-unmetered-connection.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Nice=10
|
||||
ExecStart=/usr/local/sbin/restic_backup.sh
|
||||
# $HOME or $XDG_CACHE_HOME must be set for restic to find /root/.cache/restic/
|
||||
Environment="HOME=/root"
|
||||
13
etc/systemd/system/restic-backup@.service
Normal file
13
etc/systemd/system/restic-backup@.service
Normal file
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Backup with restic to Backblaze B2
|
||||
OnFailure=status-email-user@%n.service
|
||||
Requires=nm-unmetered-connection.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Nice=10
|
||||
# $HOME or $XDG_CACHE_HOME must be set for restic to find /root/.cache/restic/
|
||||
Environment="HOME=/root"
|
||||
# The random sleep (in seconds) is in the case of multiple backup profiles. Many restic instances started at the same time could case high load or network bandwith usage.
|
||||
# `systemd-cat` allows showing the restic output to the systemd journal
|
||||
ExecStart=bash -c 'sleep $(shuf -i 0-300 -n 1) && source /etc/restic/%I.env && /usr/local/sbin/restic_backup.sh | systemd-cat'
|
||||
@@ -7,4 +7,5 @@ Requires=nm-unmetered-connection.service
|
||||
[Service]
|
||||
Type=simple
|
||||
Nice=10
|
||||
ExecStart=/usr/local/sbin/restic_check.sh
|
||||
# `systemd-cat` allows showing the restic output to the systemd journal
|
||||
ExecStart=bash -c 'source /etc/restic/%I.env && /usr/local/sbin/restic_check.sh | systemd-cat'
|
||||
@@ -15,7 +15,7 @@ if [ $# -eq 0 ]; then
|
||||
echo "No program to run given!" >&2
|
||||
exit 1
|
||||
fi
|
||||
cmd="$@"
|
||||
cmd="$*"
|
||||
|
||||
body=$(eval "$cmd" 2>&1)
|
||||
|
||||
|
||||
@@ -1,9 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
# Make backup my system with restic to Backblaze B2.
|
||||
# This script is typically run by: /etc/systemd/system/restic-backup.{service,timer}
|
||||
# Make a backup with restic to Backblaze B2.
|
||||
#
|
||||
# This script is typically run (as root user) either like:
|
||||
# - from restic service/timer: $PREFIX/etc/systemd/system/restic-backup.{service,timer}
|
||||
# - from a cronjob: $PREFIX/etc/cron.d/restic
|
||||
# - manually by a user. For it to work, the environment variables must be set in the shell where this script is executed
|
||||
# $ source $PREFIX/etc/default.env
|
||||
# $ restic_backup.sh
|
||||
|
||||
# Exit on error, unset var, pipe failure
|
||||
set -euo pipefail
|
||||
|
||||
# Assert that all needed environment variables are set.
|
||||
# TODO in future if this grows, move this to a restic_lib.sh
|
||||
assert_envvars() {
|
||||
local varnames=("$@")
|
||||
for varname in "${varnames[@]}"; do
|
||||
if [ -z ${!varname+x} ]; then
|
||||
printf "%s must be set for this script to work.\n\nDid you forget to source a /etc/restic/*.env profile in the current shell before executing this script?\n" "$varname" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
assert_envvars \
|
||||
B2_ACCOUNT_ID B2_ACCOUNT_KEY B2_CONNECTIONS \
|
||||
RESTIC_BACKUP_PATHS RESTIC_BACKUP_TAG \
|
||||
RESTIC_BACKUP_EXCLUDE_FILE RESTIC_BACKUP_EXTRA_ARGS RESTIC_PASSWORD_FILE RESTIC_REPOSITORY RESTIC_VERBOSITY_LEVEL \
|
||||
RESTIC_RETENTION_DAYS RESTIC_RETENTION_MONTHS RESTIC_RETENTION_WEEKS RESTIC_RETENTION_YEARS
|
||||
|
||||
# Exit on failure, pipe failure
|
||||
set -e -o pipefail
|
||||
|
||||
# Clean up lock if we are killed.
|
||||
# If killed by systemd, like $(systemctl stop restic), then it kills the whole cgroup and all it's subprocesses.
|
||||
@@ -15,36 +39,20 @@ exit_hook() {
|
||||
}
|
||||
trap exit_hook INT TERM
|
||||
|
||||
# How many backups to keep.
|
||||
RETENTION_DAYS=14
|
||||
RETENTION_WEEKS=16
|
||||
RETENTION_MONTHS=18
|
||||
RETENTION_YEARS=3
|
||||
|
||||
# What to backup, and what to not
|
||||
BACKUP_PATHS="/ /boot /home"
|
||||
[ -d /mnt/media ] && BACKUP_PATHS+=" /mnt/media"
|
||||
BACKUP_EXCLUDES="--exclude-file /etc/restic/backup_exclude"
|
||||
for dir in /home/*
|
||||
do
|
||||
if [ -f "$dir/.backup_exclude" ]
|
||||
then
|
||||
BACKUP_EXCLUDES+=" --exclude-file $dir/.backup_exclude"
|
||||
# Set up exclude files: global + path-specific ones
|
||||
# NOTE that restic will fail the backup if not all listed --exclude-files exist. Thus we should only list them if they are really all available.
|
||||
## Global backup configuration.
|
||||
exclusion_args="--exclude-file ${RESTIC_BACKUP_EXCLUDE_FILE}"
|
||||
## Self-contained backup files per backup path. E.g. having an USB disk at /mnt/media in RESTIC_BACKUP_PATHS,
|
||||
# a file /mnt/media/.backup_exclude.txt will automatically be detected and used:
|
||||
for backup_path in "${RESTIC_BACKUP_PATHS[@]}"; do
|
||||
if [ -f "$backup_path/.backup_exclude.txt" ]; then
|
||||
exclusion_args+=" --exclude-file $backup_path/.backup_exclude.txt"
|
||||
fi
|
||||
done
|
||||
|
||||
BACKUP_TAG=systemd.timer
|
||||
|
||||
|
||||
# Set all environment variables like
|
||||
# B2_ACCOUNT_ID, B2_ACCOUNT_KEY, RESTIC_REPOSITORY etc.
|
||||
source /etc/restic/b2_env.sh
|
||||
|
||||
# How many network connections to set up to B2. Default is 5.
|
||||
B2_CONNECTIONS=50
|
||||
|
||||
# NOTE start all commands in background and wait for them to finish.
|
||||
# Reason: bash ignores any signals while child process is executing and thus my trap exit hook is not triggered.
|
||||
# Reason: bash ignores any signals while child process is executing and thus the trap exit hook is not triggered.
|
||||
# However if put in subprocesses, wait(1) waits until the process finishes OR signal is received.
|
||||
# Reference: https://unix.stackexchange.com/questions/146756/forward-sigterm-to-child-in-bash
|
||||
|
||||
@@ -54,30 +62,31 @@ wait $!
|
||||
|
||||
# Do the backup!
|
||||
# See restic-backup(1) or http://restic.readthedocs.io/en/latest/040_backup.html
|
||||
# --one-file-system makes sure we only backup exactly those mounted file systems specified in $BACKUP_PATHS, and thus not directories like /dev, /sys etc.
|
||||
# --one-file-system makes sure we only backup exactly those mounted file systems specified in $RESTIC_BACKUP_PATHS, and thus not directories like /dev, /sys etc.
|
||||
# --tag lets us reference these backups later when doing restic-forget.
|
||||
restic backup \
|
||||
--verbose \
|
||||
--verbose="$RESTIC_VERBOSITY_LEVEL" \
|
||||
--one-file-system \
|
||||
--tag $BACKUP_TAG \
|
||||
--option b2.connections=$B2_CONNECTIONS \
|
||||
$BACKUP_EXCLUDES \
|
||||
$BACKUP_PATHS &
|
||||
--tag "$RESTIC_BACKUP_TAG" \
|
||||
--option b2.connections="$B2_CONNECTIONS" \
|
||||
"$exclusion_args" \
|
||||
"$RESTIC_BACKUP_EXTRA_ARGS" \
|
||||
"$RESTIC_BACKUP_PATHS" &
|
||||
wait $!
|
||||
|
||||
# Dereference and delete/prune old backups.
|
||||
# See restic-forget(1) or http://restic.readthedocs.io/en/latest/060_forget.html
|
||||
# --group-by only the tag and path, and not by hostname. This is because I create a B2 Bucket per host, and if this hostname accidentially change some time, there would now be multiple backup sets.
|
||||
restic forget \
|
||||
--verbose \
|
||||
--tag $BACKUP_TAG \
|
||||
--option b2.connections=$B2_CONNECTIONS \
|
||||
--prune \
|
||||
--verbose="$RESTIC_VERBOSITY_LEVEL" \
|
||||
--tag "$RESTIC_BACKUP_TAG" \
|
||||
--option b2.connections="$B2_CONNECTIONS" \
|
||||
--prune \
|
||||
--group-by "paths,tags" \
|
||||
--keep-daily $RETENTION_DAYS \
|
||||
--keep-weekly $RETENTION_WEEKS \
|
||||
--keep-monthly $RETENTION_MONTHS \
|
||||
--keep-yearly $RETENTION_YEARS &
|
||||
--keep-daily "$RESTIC_RETENTION_DAYS" \
|
||||
--keep-weekly "$RESTIC_RETENTION_WEEKS" \
|
||||
--keep-monthly "$RESTIC_RETENTION_MONTHS" \
|
||||
--keep-yearly "$RESTIC_RETENTION_YEARS" &
|
||||
wait $!
|
||||
|
||||
# Check repository for errors.
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# Check my backup with restic to Backblaze B2 for errors.
|
||||
# This script is typically run by: /etc/systemd/system/restic-check.{service,timer}
|
||||
# Check the backups made with restic to Backblaze B2 for errors.
|
||||
# See restic_backup.sh on how this script is run (as it's analogous for this script).
|
||||
|
||||
# Exit on failure, pipe failure
|
||||
set -e -o pipefail
|
||||
# Exit on error, unset var, pipe failure
|
||||
set -euo pipefail
|
||||
|
||||
# Assert that all needed environment variables are set.
|
||||
assert_envvars() {
|
||||
local varnames=("$@")
|
||||
for varname in "${varnames[@]}"; do
|
||||
if [ -z ${!varname+x} ]; then
|
||||
printf "%s must be set for this script to work.\n\nDid you forget to source a /etc/restic/*.env profile in the current shell before executing this script?\n" "$varname" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
assert_envvars \
|
||||
B2_ACCOUNT_ID B2_ACCOUNT_KEY B2_CONNECTIONS \
|
||||
RESTIC_PASSWORD_FILE RESTIC_REPOSITORY RESTIC_VERBOSITY_LEVEL
|
||||
|
||||
# Clean up lock if we are killed.
|
||||
# If killed by systemd, like $(systemctl stop restic), then it kills the whole cgroup and all it's subprocesses.
|
||||
@@ -15,12 +29,6 @@ exit_hook() {
|
||||
}
|
||||
trap exit_hook INT TERM
|
||||
|
||||
|
||||
source /etc/restic/b2_env.sh
|
||||
|
||||
# How many network connections to set up to B2. Default is 5.
|
||||
B2_CONNECTIONS=50
|
||||
|
||||
# Remove locks from other stale processes to keep the automated backup running.
|
||||
# NOTE nope, don't unlock like restic_backup.sh. restic_backup.sh should take precedence over this script.
|
||||
#restic unlock &
|
||||
@@ -28,6 +36,6 @@ B2_CONNECTIONS=50
|
||||
|
||||
# Check repository for errors.
|
||||
restic check \
|
||||
--option b2.connections=$B2_CONNECTIONS \
|
||||
--verbose &
|
||||
--option b2.connections="$B2_CONNECTIONS" \
|
||||
--verbose="$RESTIC_VERBOSITY_LEVEL" &
|
||||
wait $!
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/usr/bin/env bash
|
||||
# Send email notification from systemd.
|
||||
# Source: https://serverfault.com/questions/876233/how-to-send-an-email-if-a-systemd-service-is-restarted
|
||||
# Source: https://wiki.archlinux.org/index.php/Systemd/Timers#MAILTO
|
||||
@@ -15,19 +15,19 @@
|
||||
# (24 * 60 * 60) / 100 = 864 second
|
||||
# One option that I used with my old Axis cameras it to use my gmx.com accunt for sending emails instead, as there are (no?) higher limits there.
|
||||
MIN_WAIT_TIME_S=900
|
||||
SCRIPT_NAME=$(basename $0)
|
||||
SCRIPT_NAME=$(basename "$0")
|
||||
LAST_RUN_FILE="/tmp/${SCRIPT_NAME}_last_run.txt"
|
||||
|
||||
last_touch() {
|
||||
stat -c %Y $1
|
||||
stat -c %Y "$1"
|
||||
}
|
||||
|
||||
waited_long_enough() {
|
||||
retval=1
|
||||
if [ -e $LAST_RUN_FILE ]; then
|
||||
if [ -e "$LAST_RUN_FILE" ]; then
|
||||
now=$(date +%s)
|
||||
last=$(last_touch $LAST_RUN_FILE)
|
||||
wait_s=$(expr $now - $last)
|
||||
last=$(last_touch "$LAST_RUN_FILE")
|
||||
wait_s=$((now - last))
|
||||
if [ "$wait_s" -gt "$MIN_WAIT_TIME_S" ]; then
|
||||
retval=0
|
||||
fi
|
||||
@@ -35,7 +35,7 @@ waited_long_enough() {
|
||||
retval=0
|
||||
fi
|
||||
|
||||
[ $retval -eq 0 ] && touch $LAST_RUN_FILE
|
||||
[ $retval -eq 0 ] && touch "$LAST_RUN_FILE"
|
||||
return $retval
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user