Intro
I need to backup my personal $HOME to my NAS cause there's a lot in there, and mostly my git projects with .env files all over. Plus some docker data
Why Not ZFS?
GREAT QUESTION! It's because I struggled getting all the syncoid/sanoid requirements installed on my Aurora OS.... I like the immutable desktop trend, but I don't understand rpm-ostree enough to properly get all the lower level kernel stuff setup in a way I trust. So because I don't have ZFS at $HOME what's a boy to do?
rsync
would probably work fine especially if I rsync-like-a-pro but I figured I could cobble together some other tech and broaden my horizons...
Enter restic
Apparently restic has been around for a while, and you can run it in a container... awesome that's all I needed to hear...
How?
I'll use a docker compose stack for this... typically I think running restic
right on the host is the way to go, but on my desktop I float between my host
and distroboxes and to keep things as simple as possible, even though less
efficient, I want to manage everything through containers as my homelab gets
built out and eventually I'll have a more full-featured ecosystem.
The Value
You can find code below and an example repo that more or less is what I use for my desktop backup but that's primarily what I was after.... backing up the data on my desktop to my NAS in a way that:
- I wouldn't have to "remember" how I was doing it
- I accomplish this by having good git repo organization and README files that explain my own usage patterns
- Tracked snapshots
- I need some kind of snapshotting -
zfs
has spoiled me, and turns outrestic
has some amount of support for it
- Running in docker would be ideal for now so that I don't need to worry about installing specific binaries across machine while I build out my patterns
With this setup I get to backup my $HOME to my NAS which contains 1. docker
volume info for all the AI workloads I run on my desktop and 2. all my
sensitive info in my git repos is at least backed up in a way that I can
recover .env
files with relative ease...
Code
Check out the Example GH Repo but I'll drop key file contents here for a quick read if you're interested in some of the setup... but the blog post mostly ends here
The Files
We have a docker-compose.yml
of course
services: backup: image: restic/restic:latest network_mode: host container_name: restic_backup hostname: restic-backup-runner env_file: - .env environment: # The location of the backup repository inside the container - RESTIC_REPOSITORY=/target # The location of the password file inside the container - RESTIC_PASSWORD_FILE=/password volumes: # --- Source and Config Mounts --- - "${BACKUP_SOURCE}:/source:ro" - "${RESTIC_PASSWORD_FILE}:/password:ro" - "./backup-and-prune.sh:/usr/local/bin/backup-and-prune:ro" # --- Persistent Data Mounts --- - "./.ssh:/root/.ssh:rw" - "./.cache:/root/.cache:rw" # Set the default entrypoint to our new script. This will be executed when # the container starts, unless overridden. entrypoint: ["/usr/local/bin/backup-and-prune"]
Your .env
file will need to look like this
``# HOST related variables # The source directory to back up (absolute path) BACKUP_SOURCE=/home/nic # --- Restic Configuration --- # The file containing the restic repository password (absolute path on host) HOST_RESTIC_PASSWORD_FILE=/home/nic/projects/personal/homelab-mono/dataops/docker/.restic-password # The SSH private key to use for connecting to the NAS (absolute path on host) # It's recommended to use a dedicated key for this purpose. SSH_PRIVATE_KEY_FILE=/home/nic/.skm/ghost/id_rsa # Container Env Vars # the container makes the sftp connection using my credentials, but nonetheless the container needs them, so this isn't envrc stuff # --- SFTP/SSH Connection Details for the NAS --- SFTP_USER=nic SFTP_HOST=ghost # The path on the NAS where the restic repository will be stored SFTP_PATH=/tank/encrypted/nas/nic-home/ # --- Restic Configuration --- RESTIC_REPOSITORY=/target RESTIC_PASSWORD_FILE=/password
And then the backup script that the container will execute is something like this:
#!/bin/sh set -e # Exit immediately if a command exits with a non-zero status. # Construct the repository path from environment variables passed by docker-compose REPO="sftp:${SFTP_USER}@${SFTP_HOST}:${SFTP_PATH}" # 1. Run the backup # ----------------- echo "--- Starting backup for ${BACKUP_SOURCE} ---" restic backup /source --verbose -r "${REPO}" echo "--- Backup complete ---" # 2. Clean up old snapshots according to the policy # -------------------------------------------------- echo "--- Pruning old snapshots --- (Policy: keep last 7 daily, 4 weekly, 6 monthly)" restic forget \ --prune \ --keep-daily 7 \ --keep-weekly 4 \ --keep-monthly 6 \ -r "${REPO}" echo "--- Backup and prune process finished successfully ---"
Systemd
Finally there's a systemd unit and timer file in there so you can setup a systemd service for the backup
service
[Unit] Description=Run Restic backup to NAS using Docker Compose # We are running this as a user service, so we assume that the system-level # docker.service is already running. After=network-online.target [Service] Type=oneshot # Set the working directory to where your docker-compose.yml and .env file are located WorkingDirectory=/home/user/nas-backup/docker # The command to execute. We use the full path to docker-compose for reliability. # You may need to adjust this path if 'docker compose' is installed elsewhere. ExecStart=/usr/bin/docker compose run --rm backup [Install] WantedBy=default.target
timer
[Unit] Description=Run Restic backup job daily [Timer] # Run daily at 2:00 AM OnCalendar=daily # Or uncomment for a specific time: # OnCalendar=*-*-* 02:00:00 # Run the backup immediately if the last scheduled run was missed (e.g., if the computer was off) Persistent=true [Install] WantedBy=timers.target