Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple profile configuration #45

Merged
merged 38 commits into from
Feb 1, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
765523c
moved env variables out into separate .env files, with inheritance fr…
mfeif Jan 7, 2019
e186cc3
oops; wrong way to declare env variable for $HOME
mfeif Jan 7, 2019
a006731
Merge remote-tracking branch 'origin/master' into multi-repo
gerardbosch Jan 14, 2022
20705a0
Extract configuration to env profiles
gerardbosch Jan 14, 2022
f136bab
Merge remote-tracking branch 'origin/master' into multi-repo
gerardbosch Jan 17, 2022
1be6878
Merge remote-tracking branch 'origin/master' into multi-repo
gerardbosch Jan 17, 2022
53e246c
Add root note on README
gerardbosch Jan 17, 2022
7a31380
Merge remote-tracking branch 'origin/master' into multi-repo
gerardbosch Jan 17, 2022
13371f8
Align with the latest additions from master
gerardbosch Jan 17, 2022
58ac90d
Test contribution to PR
erikw Jan 17, 2022
3842699
Brush up documentation in .env files
erikw Jan 17, 2022
e534752
Fix .backup_exclude per backup path
erikw Jan 17, 2022
9a889c9
Prepare CHANGELOG.md
erikw Jan 17, 2022
80ed04c
Clarify PREFIX installation
erikw Jan 17, 2022
d4c7e56
Document in restic_*.sh how to run the scripts
erikw Jan 17, 2022
f9e44fb
Make TL;DR in README more concise
erikw Jan 17, 2022
d77c042
Correct comment about restic crashing
erikw Jan 17, 2022
424bb0c
Find backup exlude files in home directores again
erikw Jan 17, 2022
a49f672
Explain how backup exclude files works in README
erikw Jan 17, 2022
9eb647d
Fix spell
gerardbosch Jan 18, 2022
b6b7764
Update usr/local/sbin/restic_backup.sh
erikw Jan 18, 2022
6c16b60
Update usr/local/sbin/restic_backup.sh
erikw Jan 18, 2022
4675d99
Extract global exclude file into env RESTIC_BACKUP_EXCLUDE
gerardbosch Jan 18, 2022
38f9d5e
Merge remote-tracking branch 'origin/multi-repo' into multi-repo
gerardbosch Jan 18, 2022
47a6155
Align suggested function name with the call-site name
gerardbosch Jan 18, 2022
d689f80
Add sudo note on make install
gerardbosch Jan 18, 2022
9c384bc
Fix exclusion args function return
gerardbosch Jan 18, 2022
066f6f9
Fix duplicate exclusion argument entries
gerardbosch Jan 18, 2022
2ba0864
Remove automatic /home/user\*/.backup_exclude feature
erikw Jan 18, 2022
ef41587
After removing the home exclusions feature, sort_unique became dead code
gerardbosch Jan 19, 2022
1494871
README update suggestions from @mfeif
erikw Jan 31, 2022
e898fef
Fix URL sytnax
erikw Jan 31, 2022
cda0230
Random sleep on service execution
erikw Jan 31, 2022
0a4475c
Notes on password setup
erikw Jan 31, 2022
c2c6e75
README note
erikw Jan 31, 2022
df411de
Replace bash RANDOM with simpler to read shuf(1)
erikw Jan 31, 2022
ccd769d
s/RESTIC_BACKUP_EXCLUDE/RESTIC_BACKUP_EXCLUDE_FILE/
erikw Jan 31, 2022
ce62520
PR feedback fixes in README
erikw Feb 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# Prevent check-in of these sensitive files. Instead they are generated from the corresponding *.template file.
/etc/restic/b2_pw.txt
/etc/restic/b2_env.sh
etc/restic/pw.txt
etc/restic/_global.env
etc/restic/default.env

# IntelliJ
.idea/
*.iml
# VSCode
.vscode/
11 changes: 5 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ SRCS_SCRIPTS = $(filter-out %cron_mail, $(wildcard usr/local/sbin/*))
SRCS_CONF = $(sort $(patsubst %.template, %, $(wildcard etc/restic/*)))
SRCS_SYSTEMD = $(wildcard etc/systemd/system/*)

# Just set PREFIX in environment, like
# Just set PREFIX var in your shell's environment, like:
# $ PREFIX=/tmp/test make
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.
Expand All @@ -35,16 +34,16 @@ install-scripts:

# 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/b2_env.sh etc/restic/b2_pw.txt:
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
# | means that dependencies are order-only i.e. only created if they don't already exist.
# `|` 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 -b -m 0600 $(SRCS_CONF) $(DEST_CONF)
$(RM) etc/restic/b2_env.sh etc/restic/b2_pw.txt
$(RM) etc/restic/_global.env etc/restic/default.env etc/restic/pw.txt

# target: install-systemd - Install systemd timer and service files.
install-systemd:
Expand Down
91 changes: 78 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[![Open issues](https://img.shields.io/github/issues/erikw/restic-systemd-automatic-backup)](https://github.com/erikw/restic-systemd-automatic-backup/issues)
[![Closed issues](https://img.shields.io/github/issues-closed/erikw/restic-systemd-automatic-backup?color=success)](https://github.com/erikw/restic-systemd-automatic-backup/issues?q=is%3Aissue+is%3Aclosed)
[![Closed PRs](https://img.shields.io/github/issues-pr-closed/erikw/restic-systemd-automatic-backup?color=success)](https://github.com/erikw/restic-systemd-automatic-backup/pulls?q=is%3Apr+is%3Aclosed)
[![License](https://img.shields.io/badge/license-BSD--3-blue)](LICENSE.txt)
[![License](https://img.shields.io/badge/license-BSD--3-blue)](LICENSE)
[![OSS Lifecycle](https://img.shields.io/osslifecycle/erikw/restic-systemd-automatic-backup)](https://github.com/Netflix/osstracker)
[![Latest tag](https://img.shields.io/github/v/tag/erikw/restic-systemd-automatic-backup)](https://github.com/erikw/restic-systemd-automatic-backup/tags)
<br>
Expand All @@ -31,7 +31,66 @@ Note, you can use any of the supported [storage backends](https://restic.readthe
# Requirements
* `restic >=v0.9.6`

# Set up
# TL;DR

There is a `Makefile` to assist in the installation by copying all required files to their proper
place. You can install everything by doing:

```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.

Once files are in place you will need to fill some configs with your own values, mostly B2
related ones. Fill in the following files accordingly (you will need to edit them with **sudo**):

- `/etc/restic/pw.txt` - Contains the password used by restic to encrypt the
- repository files.
- `/etc/restic/_global.env` - Global environment variables.
- `/etc/restic/default.env` - Profile specific environment variables (multiple profiles can be
defined).

🗄️ Finally, **run the backup** using Systemd:

```console
sudo systemctl start restic-backup@default
```

👀 and watch its progress with Systemd journal:

```console
journalctl -f --lines=50 -u restic-backup@default
```

ALSO, as you will be probably interested that the backup runs automatically every day, start and
persist the systemd timer that will run the backup service unit:

```console
sudo systemctl start restic-backup@default.timer
sudo systemctl enable restic-backup@default.timer
```

**Final note**: You can define different profiles to run backups, just make a copy of `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.

### Verify your backup

As the profile is owned by root and managed by systemd, you need to proceed as **root**,
e.g. `sudo -i`.

- First, you need to load your profile: `source /etc/restic/default.env`, then
- you can list your snapshots with `restic snapshots`, or
- mount the remote repo to check your files, e.g. `restic mount /mnt/restic`

You can also run a repository integrity/consistency check from time to time:
```console
sudo systemd start restic-check@default
```

# Step-by-step and manual setup

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:

Expand All @@ -53,18 +112,21 @@ Take note of the your account ID, application key and password for the next step


## 2. Configure your B2 account locally

> **Attention!** Going the manual way requires that most of the following commands are run as root.

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:
* `default.env`: 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:
```console
$ source /etc/restic/b2_env.sh
$ source /etc/restic/default.env
$ restic snapshots # You don't have to supply all parameters like --repo, as they are now in your environment!
````
* `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.
* `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.

## 3. Initialize remote repo
Now we must initialize the repository on the remote end:
```console
$ source /etc/restic/b2_env.sh
$ source /etc/restic/default.env
$ restic init
```

Expand All @@ -74,7 +136,7 @@ Put this file in `/usr/local/sbin`:

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.

Aside from system-wide exclusions, every user can define their own ones at `~/.backup_exclude`.

## 5. Make first backup & verify
Now see if the backup itself works, by running
Expand All @@ -89,16 +151,19 @@ Now we can do the modern version of a cron-job, a systemd service + timer, to ru


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).

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
Expand All @@ -113,13 +178,13 @@ $ 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)
Expand Down
18 changes: 18 additions & 0 deletions etc/restic/_global.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 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"

# Backblaze B2 credentials
export B2_ACCOUNT_ID="<b2-account-id>" # TODO fill with your account info
export B2_ACCOUNT_KEY="<b2-account-key>" # TODO fill with your account info

# How many network connections to set up to B2. Default is 5.
export B2_CONNECTIONS=10
8 changes: 0 additions & 8 deletions etc/restic/b2_env.sh.template

This file was deleted.

1 change: 0 additions & 1 deletion etc/restic/b2_pw.txt.template

This file was deleted.

34 changes: 34 additions & 0 deletions etc/restic/default.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 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 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 will fail if a specified path is not mounted, thus we shoud only add it to BACKUP_PATHS if it is available.
#test -d /mnt/media && BACKUP_PATHS+=" /mnt/media"

# A tag to identify backup snapshots.
export 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 RETENTION_DAYS=14
export RETENTION_WEEKS=16
export RETENTION_MONTHS=18
export RETENTION_YEARS=3
1 change: 1 addition & 0 deletions etc/restic/pw.txt.template
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<restic-encryption-password>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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"
# `systemd-cat` allows showing the restic output to the systemd journal
ExecStart=bash -c 'source /etc/restic/%I.env && /usr/local/sbin/restic_backup.sh | systemd-cat'
Original file line number Diff line number Diff line change
Expand Up @@ -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'
36 changes: 7 additions & 29 deletions usr/local/sbin/restic_backup.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/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}

Expand All @@ -15,37 +16,14 @@ 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"
# Example below of how dynamically add a path that is mounted e.g. external USB dis.
[ -d /mnt/media ] && BACKUP_PATHS+=" /mnt/media"

# Set up exclude files: global + path-specific ones.
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"
exclusion_args="--exclude-file /etc/restic/backup_exclude"
for backup_path in ${BACKUP_PATHS[@]}; do
Copy link
Contributor Author

@gerardbosch gerardbosch Jan 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question here: What will happen if BACKUP_PATHS=/home/ and, then there is a /home/someuser/.backup_exclude file? The file makes reference to a specific user, while the paths refer to the higher level directory /home.

The previous behaviour takes the someuser exclusion file in consideration, but after the change what will happen? I think it will be omitted, right? If that's right, may be confusing. What do you think? :D

Maybe aside of backup_paths/.backup_exclude, also all user homedir .backup_exclude can be considered.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah this was the original intention, got-cha! Thanks

I had forgotten. We should look for .backup_exclude for each mount-point, and additionally each user dir in /home, if /home is in the backup path.

Will fix!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool 😊

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, please check 424bb0c

I tried it out locally, seemed to work... 😅

if [ -f "$backup_path/.backup_exclude" ]; then
exclusion_args+=" --exclude-file $backup_path/.backup_exclude"
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.
# However if put in subprocesses, wait(1) waits until the process finishes OR signal is received.
Expand All @@ -64,7 +42,7 @@ restic backup \
--one-file-system \
--tag $BACKUP_TAG \
--option b2.connections=$B2_CONNECTIONS \
$BACKUP_EXCLUDES \
$exclusion_args \
$BACKUP_PATHS &
wait $!

Expand All @@ -75,7 +53,7 @@ restic forget \
--verbose \
--tag $BACKUP_TAG \
--option b2.connections=$B2_CONNECTIONS \
--prune \
--prune \
--group-by "paths,tags" \
--keep-daily $RETENTION_DAYS \
--keep-weekly $RETENTION_WEEKS \
Expand Down
6 changes: 0 additions & 6 deletions usr/local/sbin/restic_check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,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 &
Expand Down