Off-site Backup Solution

While the basic technique used in this article is still used, the exact implementation has since changed and is now available here: https://git.sr.ht/~witcher/backup

Quite a while ago I set up a backup solution that works for me but isn’t ideal. I have an external hard drive that I plug into my workstation, run my usual backup command using restic, and disconnect it again.
This works, but it’s not handy. Getting the drive, connecting it, running the command, waiting for it to finish, ejecting the drive, and unplugging it is tedious and so I only do this about once a month.

Add to this that the backup lives essentially right next to the workstation, so it only protects me from malware or oopsies on my end. If my workstation ends up in flames, the house collapses, a thief breaks in, or similar, the backup is of no use.
As a “soft backup”, a lot of my important files are on my Nextcloud instance. I call it a “soft backup” because this is live data, not a backup, but it can act as one; although this is not recommended.

A few days ago, I set up a backup solution that uses off-site storage to store my backups. Precisely, it’s the Storage Box BX11 from Hetzner, which gives me 1TB of storage for a little less than 4€ a month. Crucially, it supports SFTP, a protocol that restic can use as a backend to interact with a repository.
After a long first backup I now feel like my data is safe. It’s not perfect, but it’s good enough.

What I still have to figure out, though, is how I guarantee that a backup is made daily, because that is what I want. I can’t just create a cronjob for 3am because I shut my workstation down if I don’t use it to not waste any power. But I also don’t use my workstation at the same time each day, so I really can’t ensure that a backup is run daily with cron.
Alternatively, as I am using Arch Linux which comes with systemd, I could set up a systemd service that fires when my workstation boots, but since I don’t want to use systemd and it could be that I start the system more than once a day, this doesn’t seem like a good option.

Again, I still have to figure out how to automatically run the backup, but for now, just running my script is fine:

#!/bin/sh -eu

exec >> /var/log/backup/remote.log 2>&1
date

# backup home directory to external server using restic.
# steps:
#   1. backup
#   2. keep last x snapshots

. "${XDG_CONFIG_HOME:-${HOME}/.config}/backup/remote/config.rc"

backup() {
    printf "Backing up ${BACKUP_DIRECTORY} to remote ${REMOTE_HOSTNAME}\n"

    restic "${RESTIC_OPTIONS}" \
        backup \
        --exclude-file "${EXCLUDE_FILE_PATH}" \
        --files-from "${FILES_FROM_PATH}" \
        "${BACKUP_DIRECTORY}"
}

forget() {
    printf "Running forget, keeping last ${REMOTE_FORGET_KEEP} snapshots\n"

    restic "${RESTIC_OPTIONS}" \
        forget \
        --keep-last="${REMOTE_FORGET_KEEP}"
}

backup
forget

My configuration file looks like this:

REMOTE_PROTOCOL="sftp"
REMOTE_USERNAME="user"
REMOTE_HOSTNAME="backup.example.org"
REMOTE_URL="${REMOTE_PROTOCOL}:${REMOTE_USERNAME}@${REMOTE_HOSTNAME}"
REMOTE_REPOSITORY_PATH="./workstation/backup"
REMOTE_FORGET_KEEP="30"

CONFIG_HOME="${XDG_CONFIG_HOME:-${HOME}/.config}"
CONFIG_PATH="${CONFIG_HOME}/backup/remote"

REPOSITORY_PASSWORD="supersecretpassword"
EXCLUDE_FILE_PATH="${CONFIG_PATH}/excludes.txt"
FILES_FROM_PATH="${CONFIG_PATH}/files_from.txt"
BACKUP_DIRECTORY="${HOME}"

RESTIC_OPTIONS="--quiet"

export RESTIC_REPOSITORY="${REMOTE_URL}:${REMOTE_REPOSITORY_PATH}"
export RESTIC_PASSWORD="${REPOSITORY_PASSWORD}"

Feel free to use both if you have any use for them.

Update 2023-02-06

I have now successfully set up daily automatic backups with anacron(8), which works like cron(8) but doesn’t assume that the machine it is running on is running continuously.
Thanks to rkta for mentioning this!

Do you have a comment on one of my posts? Feel free to send me an E-Mail: witcher@wiredspace.de
To participate in a public discussion, use my public inbox: ~witcher/public-inbox@lists.sr.ht (Archive)
Please review the mail etiquette.

Posted on: February 05, 2023

Articles from blogs I read

Enabling the next-generation trait solver on nightly

After nearly 4 years of active development, the next-generation trait solver is close to stabilization. We are enabling it by default on nightly to surface any remaining issues and plan to stabilize it in the next months. This is the largest single change…

via Rust Blog August 21, 2026

The brief history of lsp_lines.nvim

Back in 2022, LSP implementations could find errors and other diagnostics in code and send them into the editor, but editors lacked good interfaces to actually present this. The best at the time was a panel with a list of errors (not unlike what Eclipse and …

via Hugo's weblog on WhyNotHugo (雨果) August 18, 2026

OpenSSH 10.5 released

In another move that signals a new OpenBSD release is in the works, the OpenSSH project has released without fanfare its new version, OpenSSH 10.5. The release notes read, OpenSSH 10.5/10.5p1 (2026-08-11) OpenSSH 10.5 was released on 2026-08-11. It is avail…

via OpenBSD Journal August 11, 2026

Generated by openring