I recently bought a NAS. This is new for me, because I’ve always built my own storage solutions using Linux servers. The last iteration was an Intel NUC with a few USB-disks attached. I was often anxious about the state of the disks, and replaced the big backup disks a few times. Last time, I decided it was time to upgrade. So, I ordered a Synology NAS. I’ve heard many good things about Synology and wanted a solution that would allow me to hot-swap faulty disks. The model I now have is a DS918+ with 4 disks.
In general, I like the idea of the NAS: a lot is just working out of the box and it’s “just Linux” underneath. It has an easy-to-use interface and many packages available. However, the default backup strategy seems to be for your devices to push their stuff to the NAS. While this is fine for machines I log into regularly (like a desktop machine) and that tell you if a backup failed or not, for my servers I want my backup solution to pull the data instead. Mostly, because this will alert me when a backup fails. On my NUC, I used the fantastic rsnapshot utility, that uses a combination of rsync and hard links to build incremental backups. The Synology NAS does not have something for that though, so I set out to build it myself! This post documents what I did, in the hope that it helps others as well.
The solution I ended up with depends on Docker, so I went to the Package Center and installed it. I also use the command line quite a bit, so I enabled SSH in Control Panel > Terminal & SNMP
. In order to run docker
as a user, I created a group “docker” using the Group
Control Panel and added myself to it. Docker had to be restarted, which you can do from the Package Center interface1.
I created a Backups
shared folder with an rsnapshot
directory in there. This directory needs to have standard Linux permissions, instead of using the Synology’s default ACL solution. chmod 750 rsnapshot
from an SSH session did the trick. This is really important—I struggled with weird permission issues until I figured this out2.
Dockerfile
I also created a docker/rsnapshots
directory, that I use as the home for my Dockerfile
and other files such as rsnapshot.conf
and some helper scripts.
The Dockerfile
is pretty easy:
FROM debian:stretch RUN apt-get update -y && apt-get install -y rsnapshot ADD --chown=root:root etc/rsnapshot.conf /etc/rsnapshot.conf ADD --chown=root:root ssh/id_rsa /root/.ssh/id_rsa ADD --chown=root:root ssh/config /root/.ssh/config RUN ssh-keyscan -H HOST >> /root/.ssh/known_hosts RUN chmod 0600 /root/.ssh/config /root/.ssh/id_rsa
I copied the rsnapshot.conf
file from my previous backup-machine to a directory etc
and add that to the docker image. This means that if you change your config, you need to rebuild your docker image. Not a big deal, but I might change this in the future if it annoys me. I also created an SSH key that I’ll be using to connect to the servers with and some SSH config that specifies which user to use and the IdentityFile
.
Note that I use ssh-keyscan
to add the host key of my Linux server to the known_hosts
file. If you use this approach, you need to add your own lines with the hosts you want to connect with.
To build the docker image, I used docker build -t rsnapshot:latest .
from the directory that contains the Dockerfile
.
We are now ready to actually run rsnapshot
! To make life a bit easier, I created a quick shell script run_rsnapshot.sh
:
#!/bin/bash mkdir -p /tmp/rsnapshot >/dev/null 2>&1 RSNAP=$(docker run -v /volume1/Backups/rsnapshot/:/backup/ -v /tmp/rsnapshot:/var/run -i rsnapshot /usr/bin/rsnapshot "$*" 2>&1) EXITCODE=$? if [ -z "$RSNAP" ]; then exit $EXITCODE fi echo "$RSNAP" echo "Exited with code $EXITCODE" exit 1
Basically, this script runs /usr/bin/rsnapshot
in the docker container with the argument you give on the command line (usually something like hourly
). In order to make sure that we don’t have overlapping runs, we store the PID-file on the host. This is accomplished by creating a directory in /tmp
and passing that along to the container (-v /tmp/rsnapshot:/var/run
). The script also mounts the /volume1/Backups/rsnapshot
directory at /backup
in the container. Make sure you use /backup
in your rsnapshot.conf
!
Finally, it’s good to know that the Synology task manager works a bit differently from cron. I noticed that sometimes rsnapshot
does provide some output, but exits with a zero exit code. The Synology task-manager can either e-mail every time, or when the script exits with a non-zero exit code. I wanted to recreate the cron behaviour of e-mailing anytime there’s output, so I wrap the script and check whether the output is empty or not. If it is empty, we can just exit, otherwise we present the output again and terminate the script with exit code 1.
rsnapshot
now creates nice, incremental backups that uses hard links to ensure that only files that changed will take up extra disk space.
Using the Docker image and the power of rsnapshot
, my Synology NAS now pulls in the backups from my Linux servers. I scheduled a few recurring tasks in the Control Panel and can be sure that my backups are being tended to.
In the end, I found out that even though the NAS uses Linux underneath, there’s a lot different from a standard Debian install! The NAS uses non-standard tools like synoacltool
, synoservicectl
, etc.), it uses a different task scheduler, and the permissions are by default managed by the DSM software and ACLs. On the command-line I also miss a lot of tools like man
and less
. Luckily vim
is installed :-)
I later found out about synoservicectl
, but it’s quite hard to find out what the name of a service is… I think it should be possible to restart dockerd
with synoservicectl --restart pkg-Docker-dockerd
but haven’t really tested this.↩
It looks like the Synology NAS uses ACL to manage the permissions of files and directories. All files and directories have all Linux-permission bits set (i.e., mode 777), and docker didn’t like that. In the container, it looked like all files and directories had no permission bits set (i.e., mode 000). Bypassing the ACL by changing the mode of the rsnapshot
directory to 750 worked.↩