Introduction #
Backups often seem to be just a nice-to-have or are even completely forgotten. However, it is very important to create backups and very easy to set them up. Believe me, you will be very grateful if you ever need to use the backups from this guide.
Requirements #
To implement this guide, you need the following things:
- A Linux-based operating system (We use Debian)
- S3 compatible storage (e.g., Amazon AWS, Hetzner, Minio)
Preparations #
We want to implement the backup with Restic. Restic is a modern backup solution that runs on all common operating systems. The program is open source and offers all necessary features to operate a long-term backup concept. You can read more about Restic here .
To use Restic, you need to install it via a package manager like apt
, apk
, or dnf
.
We show you the command line commands for the mentioned package managers here:
Debian/Ubuntu #
apt-get install restic
Alpine Linux #
apk add restic
Fedora #
dnf install restic
Further installation instructions for macOS, Windows and many more can be found on the installation page .
Backup Script #
To create the backup, we use a Bash script. This script defines the necessary environment variables, the paths to be backed up, and also takes care of deleting old backups after the backup.
To store the script, we need to prepare the appropriate folder structure:
mkdir /etc/restic
Script #
/etc/restic/backup.sh
#!/usr/bin/bash
set -e
unset HISTFILE
export RESTIC_PASSWORD="<PASSWORD>"
export RESTIC_REPOSITORY="s3:<S3_PATH>"
export AWS_ACCESS_KEY_ID="<ACCESS_KEY>"
export AWS_SECRET_ACCESS_KEY="<ACCESS_SECRET>"
BACKUP_DIRS="<BACKUP_DIRECTORIES>"
/usr/bin/restic backup -v $BACKUP_DIRS 2>&1
/usr/bin/restic forget --keep-daily 14 --keep-monthly 12 --keep-yearly 3 --prune
Explanation #
Let’s go through the script in detail:
#!/usr/bin/bash
set -e
In the first two lines we define the Shebang and specify that errors should lead to immediate termination of the script.
unset HISTFILE
export RESTIC_PASSWORD="<PASSWORD>"
export RESTIC_REPOSITORY="s3:<S3_PATH>"
export AWS_ACCESS_KEY_ID="<ACCESS_KEY>"
export AWS_SECRET_ACCESS_KEY="<ACCESS_SECRET>"
In this block we use unset HISTFILE
to ensure that the environment variables defined in the script don’t end up in the Bash History
.
BACKUP_DIRS="<BACKUP_DIRECTORIES>"
This defines all folders that should be part of the backup. Any number of paths separated by spaces can be specified here. This could look like this for example:
BACKUP_DIRS="/var/lib/mysql /home/username/Documents /mnt/data/myfiles"
All paths stored here will then be backed up together. If you want to store certain data separately from each other, you can duplicate or extend the script accordingly.
/usr/bin/restic backup -v $BACKUP_DIRS 2>&1
This command starts the actual backup. The -v
flag gives us debug output that contains more information
about the execution. Via $BACKUP_DIRS
the paths defined in the previous line are passed to Restic.
2>&1
ensures that outputs for stderr
are moved to stdout
. If you want to learn more about this topic,
you can check here
.
/usr/bin/restic forget --keep-daily 14 --keep-monthly 12 --keep-yearly 3 --prune
The last line is intended to not store all backups ever created, but only to keep relevant backups. This saves storage space and thus costs. You can define how many backups should be kept via the flags.
The combination shown here keeps the backups of the last 14 days. In addition, the last backup of each month
is also kept for the last 12 months. The same applies to the yearly backups, of which
the last backup of the calendar year always remains. This is then applied to the last 3 years.
For illustration, here is an example list of the backups that would be kept on 21.06.2025
:
Flag | Backups to be kept |
---|---|
keep-daily |
All backups from 08.06.2025 to 21.06.2025 inclusive |
keep-monthly |
21.06.2025 , 31.05.2025 , 30.04.2025 , 31.03.2025 , 28.02.2025 ,31.01.2025 , 31.12.2024 , 30.11.2024 , 31.10.2025 , 30.09.2025 , 31.08.2025 , 31.07.2025 , |
keep-yearly |
21.06.2025 , 31.12.2024 , 31.12.2023 |
Additional parameters or other combinations of data to be kept can be read in the Restic documentation .
Testing the Script #
Before we can use the repository, we need to initialize it. Set the environment variables from the script
and run restic init
. The output should look something like this:
root@host:/etc/restic# restic init
created restic repository 67dc85d75b at s3:s3-location.de/bucket/blog
Please note that knowledge of your password is required to access
the repository. Losing your password means that your data is
irrecoverably lost.
After saving, make the script executable with chmod +x ./backup.sh
and run it.
If everything is set up correctly, the output should look like this and you can proceed with setting up the
cron job.
root@msadm01:/etc/restic# ./backup.sh
open repository
repository 67dc85d7 opened (repository version 2) successfully, password is correct
created new cache in /root/.cache/restic
lock repository
no parent snapshot found, will read all files
load index files
start scan on [/tmp/test]
start backup on [/tmp/test]
scan finished in 0.619s: 0 files, 0 B
Files: 0 new, 0 changed, 0 unmodified
Dirs: 2 new, 0 changed, 0 unmodified
Data Blobs: 0 new
Tree Blobs: 3 new
Added to the repository: 710 B (673 B stored)
processed 0 files, 0 B in 0:00
snapshot 479420ac saved
repository 67dc85d7 opened (repository version 2) successfully, password is correct
Applying Policy: keep 14 daily, 12 monthly, 3 yearly snapshots
keep 1 snapshots:
ID Time Host Tags Reasons Paths
----------------------------------------------------------------------------------
479420ac 2025-06-26 14:18:08 hostname daily snapshot /tmp/test
monthly snapshot
yearly snapshot
----------------------------------------------------------------------------------
1 snapshots
Cron Job #
To save the log files, we need to create the folder once:
mkdir /var/log/restic-client/
To automatically start the backup every night, we use a cron job in this example. This is edited with
crontab -e
and set up as follows:
10 0 * * * /etc/restic/backup.sh >> /var/log/restic-client/$(date '+\%Y-\%m-\%d_\%H-\%M-\%S').log 2>&1
| | | | |
| | | | |
| | | | +---- Day of the Week (range: 1-7 or SUN-SAT)
| | | +------ Month of the Year (range: 0-11 or JAN-DEC)
| | +-------- Day of the Month (range: 1-31)
| +---------- Hour (range: 0-23)
+------------ Minute (range: 0-59)
Below the entry for the job, the explanations of the individual time markers are inserted.
These serve only for information and should not be copied.
The job is started every night at 00:10
and executes our backup shell script.
The output is written to a log file under the path /var/log/restic-client/
using >>
. This is labeled with the
corresponding date and time when the job is started. At the end, 2>&1
specifies
that all outputs to stderr
are also redirected to stdout
. This guarantees that all
outputs end up in the log file and are saved. You can get more information about this here
Now the backup setup should be complete and your paths will be automatically backed up to S3 every night.
Good luck with implementing your backup strategy. If you have questions or suggestions, don’t hesitate to contact me through the channels listed below.