Thumbnail image

Nextcloud Backup pt. 3

Mon, Dec 27, 2021 16-minute read

Table of Contents

This article is the final part of a 3-part series on automating a backup for Nextcloud. If you arrived straight here, please make sure to visit the previous articles in the series as I won’t be repeating the general process, but building upon the previous posts.

Previously in this series:


Buy Me A Coffee

Thank you for visiting my site and checking out this post! I hope you find it helpful. You may have noticed I don’t have any advertisements running (I hate how invasive online advertising has become). This also means no passive income to keep the site running. Please consider donating a small amount to say thank you and help me cover the costs.


Overview

In the last article, we setup a remote Restic repository and simplified our backups with environmental variables. This is all to set us up for the final piece of automatic backups:

  • Create a non-root user to execute the backups for some added security
  • Automate the backup process with a script + cron

While these are only two steps, there are complexities involved in each. You will notice that at the end of each section I run a small test to ensure my previous efforts were successful. The more complex we get, the better idea it is to test as we go. Hopefully this will reduce massive debugging headaches later!

Create a Non-Root Restic User

Back to our server with Nextcloud and Restic. In the previous article, we downloaded and installed Restic from the Debian/Ubuntu repositories, but it meant that to read all of the files we wished to backup, it had to be run as root, or a user capable of reading said files. Let’s remedy that.

The first part of this tutorial is taken directly from the examples section in the Restic documentation.

Remove existing Restic install (Optional)

We can remove the installed version of Restic to reduce confusion:

$ sudo apt remove restic
remove restic from system

Remove installed versions of Restic.

Create a New System User

The user we create needs to have access to Nextcloud and our database for dumps as shown in the previous article. This usually means adding the user to the correct group.

$ sudo adduser --system --group restic
$ sudo usermod -aG docker restic

Note:

  • The first command creates a system user named restic and creates the restic group as well.
  • The second command ensures the user has access to docker to use docker exec commands.
add restic system user and docker

Create a new restic user and add to the docker group.

$ sudo adduser --system --group restic
$ sudo usermod -aG www-data restic

Note:

  • The first command creates a system user named restic and creates the restic group as well.
  • The second command ensures the user has access to files owned by the www-data group. This is the default Apache2 user in Debian/Ubuntu installs, and the one who has access to our Nextcloud files. If you are using a different distribution, the group name might need to be different.
add restic system user and www-data

Create a new restic user and add to the www-data group.

Install Restic Program (Binary)

Instead of installing Restic onto the entire system directly from the Debian/Ubuntu repositories, we will instead find the binary directly from the Restic team on Github.

$ sudo mkdir -m 777 ~restic/bin
$ sudo curl -L https://github.com/restic/restic/releases/download/v0.12.1/restic_0.12.1_linux_amd64.bz2 \
  | bunzip2 > /home/restic/bin/restic

Note:

  • The first command is creating a new directory to store the Restic binary. Note we are using some bad permissions, but that’s just to correctly copy over the program after downloading.
  • The second command pulls the latest (version 0.12.1) version of Restic from GitHub and unzips it into the newly created directory.
pull restic from GitHub

Installing a fresh copy of Restic.

How can I find which version of Restic is the ’latest'?
Restic has a built-in self-update option that you can run regardless of what version you are currently using! Simply use the restic self-update command which will pull the latest release from the Restic team’s GitHub page.

Set Strict Permissions

Before we continue we want to fix those loose permissions and instead use strict permissions (limited access) for the Restic binary.

$ sudo chown -R root:restic /home/restic/bin/
$ sudo chmod -R 750 /home/restic/bin/

Note:

  • The first command is changing the ownership of the bin directory and the restic program inside of it.
  • The second command gives the owner of the file (root) full access to it (read, write, execute), and the group (restic) read and execute access.
set permissions for the binary

Set strict permissions for Restic.

Test Restic

While we can easily move onto the next step, I prefer to double-check my work as I go. This prevents me from running into issues later and not knowing where they came from.

A simple test is to run Restic’s self-update program and see what version it is on. The key is to run it as the restic user to make sure we have permissions correct.

$ sudo -u restic /home/restic/bin/restic self-update
$ sudo -u restic /home/restic/bin/restic version
Note:
While not commonly used, we can use sudo with the -u switch to run a command as a specific user!
check the restic binary is working

Run a few restic commands to ensure it works.

Success!

Prepare For Automation

If you’ve been following along from the previous articles, we created some helper files to assist in the usage of our Restic backups: an environmental variables file and a set of include/exclude files. Let’s move those so we can access them easily.

Move Restic files from Root to Restic user

$ sudo mv /root/.restic.env /root/restic.include /root/restic.exclude /home/restic/

This single command will move your .restic.env, restic.include and restic.exclude files to the newly created user’s home directory.

move restic helper files

Move our Restic helper files to the new user.

Set Permissions

Let’s ensure the restic user has access to the files we just moved.

$ sudo chown root:restic /home/restic/.restic.env /home/restic/restic*
$ sudo chmod 640 /home/restic/.restic.env /home/restic/restic*

Note:

  • For a bit of added security, we set the owner of the files to the root user and we only give write permission to root.
  • We give the restic group access to the files, but make them read only.

You can set your files more permissive, but DO NOT give any access to ‘other’ users (keep the last digit 0 in the above command!)

set restic files permissions

Set our permissions for restic to read the files.

Verify File Access

Once again we will test as we go. A simple test is whether or not the restic user can read the files we just played with:

$ sudo -u restic tail /home/restic/.restic.env /home/restic/restic.*

If everything went according to plan, we should see the output of the files we moved. If you get a permission denied error, head back above and fix the file that the restic user is unable to read.

restic user can read the files correctly

While not strictly necessary, always a good double-check.

Technical Modifications

In this section, we must address issues with running as a non-root user. Unfortunately, there are some things that do require more privileges than the average user. One applies to docker based installs, while the other is for bare metal installations.

DOCKER ONLY - Give Restic Program “Read” Access to the System

By default, the Restic program is only able to read (access) files that belong to the user running the program. This is a bit problematic as the files we want to backup are owned by multiple different system users.

restic cannot read files

Restic denied access to our Nextcloud data!

Since our restic user will be running the program, it won’t have access to our Nextcloud or database backup files. There are a few approaches to this problem. Without delving into the pro’s and con’s of each, I have decided to go for a simple approach. We can use a special command to allow the restic program to read (and therefore backup) the whole file system.

Warning:
Anyone in the restic group would be able to use the Restic program to access any file on the system! This is known as capability elevation and can be a security risk in some systems. You have been warned.
$ sudo setcap cap_dac_read_search=+ep /home/restic/bin/restic
Note:
This setting only applies to this exact program file. If it is deleted, re-installed, or updated, you will need to run the command again to allow access again.

This command gives the restic binary a way to bypass a basic function of Linux security known as discretionary access control (DAC). This allows the program to act like the root user to read any file, and read/execute any directory. It is one reason that we must protect the program with strict permissions.

Test with a Simple Backup

After setting the above, we can either do a full backup (what I attempted in the image above), or just do a quick test run:

$ umask 007
$ touch /tmp/test
$ ls -l /tmp/test
$ sudo -u restic cat /tmp/test
$ sudo -u restic /bin/sh -c '. ~restic/.restic.env && ~restic/bin/restic backup /tmp/test'
$ sudo -u restic /bin/sh -c '. ~restic/.restic.env && ~restic/bin/restic forget ba19dcd6'
restic can read files

Restic is now capable of reading files the owner cannot access.

This simple test just shows that the restic user does not gain any special capabilities, but only the restic program does.

With these preliminary steps in place, we can move forward to automating the entire backup process! Skip ahead.

BARE METAL ONLY - Allow Restic to Toggle Maintenance Mode

While giving the restic user access to the www-data group will allow us to backup the Nextcloud files, it won’t allow us to execute a critical command. Before creating a database backup, it is important we temporarily put Nextcloud into maintenance mode. This will reduce the chances of a corrupted database and file errors.

While this could be executed other ways, I have gone with a simple approach. We will give our restic user the ability to run the two necessary commands as the www-data user and nothing more.

We begin by adding to our sudoers file. In Ubunutu, we can create a specific file in the /etc/sudoers.d/ directory that will be read when using the sudo command. Other distributions might require direct modification of the /etc/sudoers file.

$ sudo visudo -f /etc/sudoers.d/restic
Note:
Using visudo ensures the file has no syntax errors before placing it into the correct directory.

To this new file, there’s a specific syntax explained below. Remember that sudo is much more fine grained than ONLY allowing the user to run commands as root.

1
restic ALL = (www-data) /usr/bin/php /var/www/nextcloud/occ maintenance\:mode --on, /usr/bin/php /var/www/nextcloud/occ maintenance\:mode --off

Note:

  • First is the user we want this to apply to - restic.
  • Next is the host that this can be run on. ALL can be replaced with your hostname.
  • (www-data) implies that this command can only be run as the indicated user.
  • The next are the two commands we will need to run to put Nextcloud into and out of maintenance mode. Note that we have to escape the : by making it \:.

If after saving and closing you get no feedback, everything is OK. Otherwise it will alert you to a syntax error and you can press e to go back and edit the file again.

modified sudoers file

No errors shown after creating a sudoers file.

Quick Test of occ Command

A quick test to ensure we have everything working correctly is to simply run the two commands we just enabled for our restic user. We can use the same trick as above to run the command as the restic user.

$ sudo -u restic sudo -u www-data /usr/bin/php /var/www/nextcloud/occ maintenance:mode --on
$ sudo -u restic sudo -u www-data /usr/bin/php /var/www/nextcloud/occ maintenance:mode --off
restic using occ

Restic successfully executing our occ commands!

With these preliminary steps in place, we can move forward to automating the entire backup process!

A Look at Scripting

Now that we have everything in place, we are going to get to the heart of the article, which is scripting actions. Once we have a working script, can have it run at regularly scheduled intervals with tools like systemd or cron.

What is scripting?
In short, a script is like a list of directions (or a recipe) we would like the system to follow. It is in some senses a very basic program that we can write. In this case, we will be using the same commands as we have in the past articles, but instead of typing or copying them one-by-one, the script will run them for us!

The first half of the script will take care of our Nextcloud database backup. And, since Nextcloud’s data is stored directly on our filesystem, we will have everything we need to begin the backup with Restic in the second half of the script. To round it out, we will do some cleanup of the Nextcloud database backup file and our Restic repository.

If this all sounds terrifying, I recommend taking some time to learn the basics of bash scripting. It should only take a couple of hours to be familiar with what I am doing in this article. Or, you can contact me directly as I offer 1:1 instruction and can help walk you through the process step-by-step.

Recreating Database Backups

To begin, let’s recreate the steps we went through in the first article of this series.

  • I will be writing the Docker portion from the perspective of a user with Nextcloud running in a Docker container and using MariaDB/MySQL in a second container. You may need to adjust your script accordingly.
  • I will be writing the Bare Metal portion from the perspective of a user with both Nextcloud and MariaDB/MySQL running on the same server.
create a new script

Start a new script with your favorite editor.

Open a new document with your favorite editor like nano or vim. I will call it nextcloud-backup.sh and place it in the /home/restic directory. The first line of a bash script will always include a special line which tells your server how we want this script to be run:

1
#!/bin/bash

Below this will begin our instructions. They should look familiar if you’ve been following along with my articles. For an explanation of each line, refer to the article linked above.

 2
 3
 4
 5
 6
 7
 8
 9
10
11

/usr/bin/docker exec -u www-data nextcloud php occ maintenance:mode --on

/usr/bin/docker exec -u mysql nextcloud-db mkdir -m 750 /var/lib/mysql/backup

/usr/bin/docker exec -u mysql nextcloud-db bash -c 'umask 007 && mysqldump --single-transaction \
  -u nextcloud -p`cat "$MYSQL_PASSWORD"` nextcloud > \
  /var/lib/mysql/backup/nextcloud-sqlbkp_`date +"%Y%m%d"`.bak'

/usr/bin/docker exec -u www-data nextcloud php occ maintenance:mode --off

Note:

  • The first and last commands apply to the nextcloud container.
  • The middle two commands apply to the Nextcloud database container. Adjust the names to your setup accordingly.
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12

umask 077

sudo -u www-data /usr/bin/php /var/www/html/occ maintenance:mode --on

mkdir /tmp/dbbackup

mysqldump --single-transaction -u nextcloud -pMYSECRETPASSWORD \
  nextcloud > /tmp/dbbackup/nextcloud-sqlbkp_`date +"%Y%m%d"`.bak

sudo -u www-data /usr/bin/php /var/www/html/occ maintenance:mode --off
Note:
I used fairly generic names above, you will need to adjust the names/paths to your setup accordingly.

Recreating Restic Backups

For the next part of the script, we will recreate the steps from the second article of this series.

13
14
15
16
17
18
19

source /home/restic/.restic.env

/home/restic/bin/restic backup --files-from=/home/restic/restic.include \
  --exclude-file=/home/restic/restic.exclude --tag automated

/home/restic/bin/restic forget -d 7 -w 5 -m 12 -y 10 --prune
Note:
Remember that the last line will download and repack any changes to optimize your storage. This could incur huge charges if done daily on certain storage providers!

Final Touches

The last piece of the script will just be some tidying up of our file system.

20
21
22
23

/usr/bin/docker exec -u mysql nextcloud-db rm -rf /var/lib/mysql/backup

exit
Final docker script

The completed docker script.

20
21
22
23

rm -r /tmp/dbbackup

exit
finished script for bare metal

The completed script for a bare metal backup.

And that’s it! You can now save the script and exit.

Permissions…Again!

Before continuing, we will want to make sure it is both secure and executable by our restic user. If you haven’t already done so, move it to the restic user’s home directory.

$ sudo chown root:restic /home/restic/nextcloud-backup.sh
$ sudo chmod 750 /home/restic/nextcloud-backup.sh

Test Run

As we did before, let’s give our script a test run to ensure we didn’t make any typos or include incorrect paths.

$ sudo -u restic /home/restic/nextcloud-backup.sh

If all goes well, you will see some feedback onscreen. If any errors occur, best to revisit the script and fix the issue before proceeding.

test run our script

Look carefully to ensure you don’t have any errors.

Run Regularly with a Cron Job

While there are alternatives to using cron like systemd timers, cron is installed on most systems and quite easy to use. Let’s create a new cron job to run our backup nightly at 2AM.

Open crontab Editor for Restic User

$ sudo crontab -u restic -e
Note:
Here we are editing the cron jobs of another user. For more information, the ArchWiki has a great article all about cron.

Add the Backup Job

Once in the editor, head to the bottom and add a new line. The line will include the following:

0 2 * * * /home/restic/nextcloud-backup.sh 2>&1 | /usr/bin/logger -t ncbackup

Note:
The syntax is a bit odd, but is explained in the file itself and all over the internet. Basically, we are telling it we want the job to run:

  • At the 0th minute of the hour

  • The 2nd hour of the day

  • Every day of the month

  • Every month of the year

  • Every day of the week

  • Then we tell it where to find the script we want to run.

  • The final part of the line (2>&1 | /usr/bin/logger -t ncbackup) passes the output of our script (and any errors) to the system’s logging program with a “tag” attached to each entry.

Save and exit!

adding the restic backup to cron

The syntax for cron is a bit odd but very powerful.

Double Check the Job Works

If you are new to cron or don’t trust it, you can run the job in 5 minutes from now and watch to make sure everything runs smoothly. Then edit your crontab again to set it at an appropriate time. Otherwise, check the following morning that everything ran correctly. You can check:

  • Your syslog for cron jobs that have run, using grep to search for items tagged with ncbackup.
  • Your restic repository to see that a new snapshot was created (when and with a tag)
  • Verify by mounting your backup and searching through the files

grep for restic cron jobs
endo of grep cron

Using grep we can find the evidence of our script.

Conclusion

This has been a wild ride of a series for me. I hope you found some helpful tips along the way. If you are new to self-hosting or even Linux in general, I know how overwhelming this can all be. But don’t let this be the end!

Modify the Script

This series was written to give you a starting point. While it primarily focused on backing up a Nextcloud instance, the same can be used for really any self-hosted application. For a bit more complexity, you can see a script in my GitHub repo with the ability to backup multiple databases and aplications.

Verify Regularly

Restic has a nice built-in test feature to see if your data is still in good condition. But that won’t necessarily tell you if there was a problem created elsewhere. I recommend setting a calendar event to remind you to check your backups at least a couple times per year.

A few months back a friend asked for some old photos I had stored on my Nextcloud. To my surprise, the files weren’t loading. Upon further digging, I found the files at 0b each on the file system! I had no idea that I had lost some photos. Fortunately, I dug through my old backups and found the files in perfect condition from a backup made 8 months prior. Restoring the files took some time but reassured me that this process works.

If you see a mistake or have any questions, leave a comment below. I look forward to hearing from you and keep self-hosting!


Thank you for visiting my site and checking out this post! I hope you found it helpful. If you are feeling generous, please consider a small donation to help keep the server running.

Buy Me A Coffee