--- title: "How to Set Up rsnapshot Incremental Backups on Ubuntu 24.04" description: "Configure rsnapshot on Ubuntu 24.04 to keep daily, weekly and monthly snapshots of local and remote servers, schedule them with cron and restore lost files." category: "Backup Recovery" language: en source: https://cubepath.com/docs/backup-recovery/incremental-backup-with-rsnapshot updated: 2026-09-25 --- # How to Set Up rsnapshot Incremental Backups on Ubuntu 24.04 rsnapshot is a backup tool built on top of rsync. Each run produces a directory that looks like a full copy of your data, but files that did not change since the previous snapshot are stored as hard links, so they take no extra space. You get a browsable history of daily, weekly and monthly backups for little more than the size of one copy plus the changes. In this tutorial you will install rsnapshot on an Ubuntu 24.04 backup server, keep snapshots of its own configuration and data, pull backups from a second server over SSH with a read-only key, include a consistent database dump, schedule everything with cron, and restore files from a snapshot. ## Prerequisites To follow this tutorial, you will need: - A server running Ubuntu 24.04 LTS that will store the backups, with a non-root user with `sudo` privileges. A CubePath VPS with an additional block storage volume works well for this. - A dedicated disk or volume for the backups, formatted with a Linux filesystem that supports hard links (ext4 or XFS) and mounted at `/mnt/backup`. Keeping backups on the same disk as the data does not protect you against disk failure. - Optionally, a second Ubuntu server to back up remotely. This tutorial calls it `web1` with the address `web1_ip`. All rsnapshot commands run as root, because rsnapshot needs to read every file and preserve ownership. ## How rsnapshot stores snapshots rsnapshot keeps a numbered directory per retention level inside a `snapshot_root`: | Directory | Contents | |---|---| | `daily.0` | The most recent daily snapshot | | `daily.1` to `daily.6` | The six previous days | | `weekly.0` to `weekly.3` | The last four weeks | | `monthly.0` to `monthly.5` | The last six months | Only the lowest level (here `daily`) actually copies data with rsync. When `rsnapshot weekly` runs, it does not contact any server: it moves the oldest daily snapshot (`daily.6`) to `weekly.0`. The same happens between weekly and monthly. This is why the lower level must fill up before the higher levels appear. ## Step 1 - Installing rsnapshot rsnapshot is in the Ubuntu repositories and pulls in `rsync` as a dependency: ```bash sudo apt update sudo apt install -y rsnapshot ``` Check the installed version: ```bash rsnapshot version ``` ```output rsnapshot 1.4.5 ``` Confirm that the backup volume is mounted where you expect it: ```bash findmnt /mnt/backup ``` ```output TARGET SOURCE FSTYPE OPTIONS /mnt/backup /dev/vdb1 ext4 rw,relatime ``` Create the snapshot root on that volume and restrict it to root, since snapshots contain copies of sensitive files like `/etc/shadow`: ```bash sudo mkdir /mnt/backup/snapshots sudo chmod 700 /mnt/backup/snapshots ``` ## Step 2 - Configuring rsnapshot rsnapshot reads `/etc/rsnapshot.conf`. Keep a copy of the original before editing it: ```bash sudo cp /etc/rsnapshot.conf /etc/rsnapshot.conf.orig sudo nano /etc/rsnapshot.conf ``` > Important: Fields in `rsnapshot.conf` must be separated by tabs, not spaces. A line like `retain daily 7` with spaces is a syntax error. If your editor converts tabs to spaces, fix that setting before continuing. Find and change the following directives. Most already exist in the file, some commented out; leave the rest of the defaults as they are. Set the snapshot root and stop rsnapshot from creating it if it is missing. With `no_create_root`, a backup volume that failed to mount makes rsnapshot abort instead of silently filling your root filesystem: ```ini snapshot_root /mnt/backup/snapshots/ no_create_root 1 ``` Define the retention levels, from the most to the least frequent. The Ubuntu default file uses the names `alpha`, `beta` and `gamma`; comment those out and use descriptive names: ```ini retain daily 7 retain weekly 4 retain monthly 6 ``` Enable the log file and make rsnapshot use rsync's `--link-dest`, which is faster than copying with `cp -al` before each run: ```ini logfile /var/log/rsnapshot.log link_dest 1 ``` Now go to the `BACKUP POINTS / SCRIPTS` section at the end of the file. Remove or comment out the example `backup` lines and add the local directories you want to protect. The third field is a subdirectory inside each snapshot, used to group backups by host: ```ini backup /etc/ localhost/ backup /home/ localhost/ backup /var/www/ localhost/ ``` Exclude files that are large and easy to recreate. You can add `exclude` lines anywhere in the file; they apply to every backup point: ```ini exclude /home/*/.cache/ exclude node_modules/ exclude *.tmp ``` Save the file and check the syntax: ```bash sudo rsnapshot configtest ``` ```output Syntax OK ``` If you see an error such as `ERROR: /etc/rsnapshot.conf on line 97: Backup directive must contain tab`, open the file at that line and replace the spaces with tabs. ## Step 3 - Running the first snapshot Before running a real backup, use test mode (`-t`) to print the commands rsnapshot would execute without running them: ```bash sudo rsnapshot -t daily ``` ```output echo 4128 > /var/run/rsnapshot.pid /usr/bin/rsync -a --delete --numeric-ids --relative --delete-excluded \ --exclude=/home/*/.cache/ --exclude=node_modules/ --exclude=*.tmp \ /etc/ /mnt/backup/snapshots/daily.0/localhost/ ... ``` Now take the first snapshot. It copies all the data, so it takes the longest: ```bash sudo rsnapshot daily ``` The command prints nothing on success. Check the log and the result: ```bash sudo tail -n 2 /var/log/rsnapshot.log sudo ls /mnt/backup/snapshots/daily.0/localhost/ ``` ```output [2026-09-25T10:12:03] /usr/bin/rsnapshot daily: started [2026-09-25T10:14:47] /usr/bin/rsnapshot daily: completed successfully etc home var ``` Run it a second time to see the rotation and the hard links at work: ```bash sudo rsnapshot daily sudo rsnapshot du ``` ```output 2.1G /mnt/backup/snapshots/daily.0/ 3.4M /mnt/backup/snapshots/daily.1/ 2.1G total ``` The second snapshot adds only a few megabytes: unchanged files in `daily.0` and `daily.1` are the same inode on disk. You can confirm it for a single file, where both paths show the same inode number in the first column: ```bash sudo ls -li /mnt/backup/snapshots/daily.{0,1}/localhost/etc/hostname ``` ## Step 4 - Backing up a remote server over SSH rsnapshot can pull data from other servers with rsync over SSH. To read every file on `web1`, the connection must be made as root, so you will limit what the key can do with `rrsync`, a wrapper shipped with the `rsync` package that only allows rsync and, with `-ro`, only in read mode. On the backup server, create a dedicated key without a passphrase (cron cannot type one): ```bash sudo ssh-keygen -t ed25519 -f /root/.ssh/rsnapshot_ed25519 -N "" -C "rsnapshot@backup" sudo cat /root/.ssh/rsnapshot_ed25519.pub ``` Copy the printed public key. Then, on `web1`, open root's `authorized_keys`: ```bash sudo nano /root/.ssh/authorized_keys ``` Add one line with the restrictions followed by the key you copied: ```text command="/usr/bin/rrsync -ro /",restrict ssh-ed25519 AAAAC3Nza... rsnapshot@backup ``` `restrict` disables port forwarding, agent forwarding and TTY allocation, and `command=` forces every connection through `rrsync` in read-only mode. The key cannot open a shell or write files on `web1`. Ubuntu's default `PermitRootLogin prohibit-password` already allows key-only root logins. Back on the backup server, connect once to accept the host key of `web1`: ```bash sudo ssh -i /root/.ssh/rsnapshot_ed25519 root@web1_ip ``` Type `yes` to save the fingerprint. The connection closes immediately with an rrsync error, which is expected because you did not run rsync. Now tell rsnapshot to use the key. In `/etc/rsnapshot.conf`, make sure `cmd_ssh` is uncommented and set `ssh_args`: ```ini cmd_ssh /usr/bin/ssh ssh_args -i /root/.ssh/rsnapshot_ed25519 ``` Add the remote backup points, with a separate subdirectory for the host: ```ini backup root@web1_ip:/etc/ web1/ backup root@web1_ip:/var/www/ web1/ ``` Test the configuration and run a snapshot: ```bash sudo rsnapshot configtest sudo rsnapshot daily sudo ls /mnt/backup/snapshots/daily.0/ ``` ```output localhost web1 ``` ## Step 5 - Including database dumps Copying the data files of a running MySQL or PostgreSQL server does not produce a usable backup. Instead, let rsnapshot run a script that dumps the database and store the dump in the snapshot. rsnapshot runs `backup_script` entries in a temporary directory and moves any file the script writes there into the destination you name. This example assumes MySQL or MariaDB runs on the backup server itself; on Ubuntu, root can connect through the Unix socket without a password. Create the script: ```bash sudo nano /usr/local/bin/rsnapshot-mysqldump ``` ```bash #!/usr/bin/env bash set -euo pipefail # rsnapshot runs this in a temporary directory; write the dump there. mysqldump --all-databases --single-transaction --routines --events --triggers \ | gzip > all-databases.sql.gz ``` `set -o pipefail` makes the script fail if `mysqldump` fails, even though its output goes through `gzip`, and rsnapshot then reports the error. Make it executable and add it to `/etc/rsnapshot.conf`: ```bash sudo chmod 750 /usr/local/bin/rsnapshot-mysqldump ``` ```ini backup_script /usr/local/bin/rsnapshot-mysqldump localhost/mysql/ ``` After the next `sudo rsnapshot daily`, the dump appears at `/mnt/backup/snapshots/daily.0/localhost/mysql/all-databases.sql.gz`. For PostgreSQL, the same pattern works with `runuser -u postgres -- pg_dumpall | gzip > all-databases.sql.gz`. ## Step 6 - Scheduling backups with cron The Ubuntu package installs `/etc/cron.d/rsnapshot` with every line commented out. Open it: ```bash sudo nano /etc/cron.d/rsnapshot ``` Replace its contents with one line per retention level: ```text # m h dom mon dow user command 30 2 1 * * root /usr/bin/rsnapshot monthly 0 3 * * 1 root /usr/bin/rsnapshot weekly 30 3 * * * root /usr/bin/rsnapshot daily ``` The less frequent levels run first on purpose. `monthly` and `weekly` only rotate directories and finish in seconds, so they are done before `daily` starts syncing data. If two runs overlapped, the second one would stop at the lock file. Files in `/etc/cron.d` are picked up automatically; there is no service to reload. The next morning, confirm that the run happened: ```bash grep rsnapshot /var/log/syslog | tail -n 3 sudo tail -n 2 /var/log/rsnapshot.log ``` A line ending in `completed successfully` means the backup worked. With cron, a run that fails prints to standard error, and cron mails that output to root if a mail transfer agent is installed. On a server without one, check the log file regularly or have your monitoring system alert on lines containing `ERROR`. ## Step 7 - Restoring files Snapshots are plain directories, so restoring is a copy. First find which snapshots contain the file you need: ```bash sudo ls -l /mnt/backup/snapshots/*/web1/var/www/html/index.php ``` Copy it back with `rsync -a`, which keeps permissions, ownership and timestamps. To restore a single file locally: ```bash sudo rsync -a /mnt/backup/snapshots/daily.2/localhost/etc/nginx/nginx.conf /etc/nginx/nginx.conf ``` To restore a whole directory to a remote server, push it from the backup server with a normal SSH key (the rsnapshot key is read-only and will refuse writes): ```bash sudo rsync -a /mnt/backup/snapshots/daily.0/web1/var/www/ root@web1_ip:/var/www/ ``` Add `--dry-run` first to see which files would change. To restore a database dump: ```bash sudo zcat /mnt/backup/snapshots/daily.0/localhost/mysql/all-databases.sql.gz | sudo mysql ``` Test a restore every few months on a scratch server. A backup you have never restored is an assumption, not a backup. ## Troubleshooting **rsnapshot refuses to run because `snapshot_root` does not exist.** This is `no_create_root` doing its job: the backup volume is not mounted, so rsnapshot will not write to the root disk. Check `findmnt /mnt/backup` and your `/etc/fstab` entry. **`Lockfile /var/run/rsnapshot.pid exists and so does its process`.** A previous run is still going, usually because the daily sync takes longer than expected. Wait for it to finish. If the file is left over after a crash and no rsnapshot process is running (`pgrep rsnapshot` prints nothing), remove it with `sudo rm /var/run/rsnapshot.pid`. **`rsync error: some files/attrs were not transferred (code 23)`.** rsnapshot logs this as a warning and still completes the snapshot. It usually means files vanished during the copy or a path in a `backup` line does not exist. Run `sudo rsnapshot -v daily` to see which files are affected. **`Permission denied (publickey)` for remote backups.** Check that `ssh_args` points to the right key, that the public key line in `/root/.ssh/authorized_keys` on the remote server is on a single line, and that the host key was accepted in Step 4. **Snapshots use much more space than expected.** Hard links only work within one filesystem, and they break when a file changes in any way, including its permissions or timestamps. Log files and databases that change daily are stored in full each time; exclude them and back up dumps instead. ## Conclusion You now have rsnapshot keeping seven daily, four weekly and six monthly snapshots of local and remote data on a dedicated volume, with a consistent database dump and a restore procedure you have tested. As next steps, copy the snapshot volume to a second location to follow the 3-2-1 rule, for example with `rsync` to another data center or an object storage tool, and add an alert that fires when `/var/log/rsnapshot.log` has not recorded a successful run in the last 26 hours.