tech_notes

A collection of notes on setups and implementations.
Log | Files | Refs | README

commit db1e0de47d06e54ff4b851853c9db508b8fa09b4
parent 3aa3c98fa82dd90ae0e7d5454e7dfe32b50ef8a6
Author: Julian Piribauer <julian.piribauer@gmail.com>
Date:   Thu, 14 May 2026 12:54:15 +0000

Adding restic doc

Diffstat:
March_install.md | 2+-
Arestic_backup.md | 174+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 175 insertions(+), 1 deletion(-)

diff --git a/arch_install.md b/arch_install.md @@ -24,7 +24,7 @@ Using parted, the commands are # parted /dev/sda ``` -```bash +```console (parted) select /dev/sdX (parted) mklabel msdos (parted) mkpart primary ext2 1MB 512MB diff --git a/restic_backup.md b/restic_backup.md @@ -0,0 +1,174 @@ +# Restic backup + +We back up the server to a local Mac using restic. +The backup is triggered automatically by the Mac via a launchd agent that runs every 30 minutes. +If the last snapshot is less than 7 days old the script exits immediately, so a full backup runs at most once a week. + +**What is backed up:** +- `/home/julian` (excludes `~/.cache`) +- `/var/www` +- `/srv/git` +- `/usr/local/bin` +- `/etc/nginx`, `/etc/postfix`, `/etc/dovecot`, `/etc/letsencrypt`, `/etc/systemd/system` + +The restic repository lives on the Mac at `~/backups/server`. + +## Communication + +The Mac and the server each need an SSH connection to the other: + +``` +Mac ──SSH──▶ Server (Mac triggers the backup by running backup.sh on the server) +Server ──SFTP──▶ Mac (restic on the server stores snapshots on the Mac) +``` + +The Mac already has an SSH key trusted by the server (used for normal login). +We additionally generate a dedicated key on the server so it can reach the Mac via SFTP. + +## Prerequisites + +We assume restic is not yet installed on the server and that `Remote Login` (SSH) is enabled on the Mac under `System Settings → General → Sharing`. + +## Install restic on the server + +```console +# pacman -S restic +``` + +## Password file + +We create a password file that restic uses to encrypt all snapshots. +Store this password somewhere safe — without it the repository cannot be decrypted. + +```console +$ echo 'your-strong-password' > ~/.restic-password +$ chmod 600 ~/.restic-password +``` + +## SSH key from server to Mac + +We generate a dedicated key pair on the server and copy the public key to the Mac: + +```console +$ ssh-keygen -t ed25519 -f ~/.ssh/id_backup -N "" +$ ssh-copy-id -i ~/.ssh/id_backup.pub julian@mac.local +``` + +Test the connection: + +```console +$ ssh -i ~/.ssh/id_backup julian@mac.local echo ok +``` + +## Backup directory on the Mac + +```console +$ mkdir -p ~/backups/server +``` + +## Initialise the repository + +We initialise the restic repository on the Mac from the server: + +```console +$ restic -r sftp:julian@mac.local:/Users/julian/backups/server \ + --password-file ~/.restic-password init +``` + +## Backup script + +We create `/home/julian/backup.sh` on the server: + +```bash +#!/bin/bash +REPO="sftp:julian@mac.local:/Users/julian/backups/server" +PASSWORD_FILE=~/.restic-password + +# Skip if last snapshot is less than 7 days old +LAST_JSON=$(restic -r "$REPO" --password-file "$PASSWORD_FILE" snapshots --last --json 2>/dev/null) +LAST_TIME=$(echo "$LAST_JSON" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d[-1]['time'] if d else '')" 2>/dev/null) + +if [ -n "$LAST_TIME" ]; then + LAST_EPOCH=$(date -d "$LAST_TIME" +%s 2>/dev/null) + NOW_EPOCH=$(date +%s) + [ $(( (NOW_EPOCH - LAST_EPOCH) / 86400 )) -lt 7 ] && exit 0 +fi + +restic -r "$REPO" --password-file "$PASSWORD_FILE" \ + --exclude ~/.cache \ + backup \ + /home/julian \ + /var/www \ + /srv/git \ + /usr/local/bin \ + /etc/nginx \ + /etc/postfix \ + /etc/dovecot \ + /etc/letsencrypt \ + /etc/systemd/system +``` + +```console +$ chmod +x /home/julian/backup.sh +``` + +Run it once manually to verify everything works: + +```console +$ ~/backup.sh +``` + +## Launchd agent on the Mac + +We create `~/Library/LaunchAgents/com.backup.server.plist` on the Mac. +The easiest way is to copy it from the server: + +```console +$ scp julian@server.local:/home/julian/com.backup.server.plist \ + ~/Library/LaunchAgents/com.backup.server.plist +``` + +The plist content (also at `/home/julian/com.backup.server.plist` on the server): + +```xml +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" + "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Label</key> + <string>com.backup.server</string> + <key>ProgramArguments</key> + <array> + <string>/usr/bin/ssh</string> + <string>julian@server.local</string> + <string>/home/julian/backup.sh</string> + </array> + <key>StartInterval</key> + <integer>1800</integer> + <key>StandardOutPath</key> + <string>/tmp/server-backup.log</string> + <key>StandardErrorPath</key> + <string>/tmp/server-backup.log</string> +</dict> +</plist> +``` + +Load the agent: + +```console +$ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.backup.server.plist +``` + +Verify it is registered: + +```console +$ launchctl list com.backup.server +``` + +To reload after editing the plist: + +```console +$ launchctl bootout gui/$(id -u)/com.backup.server +$ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.backup.server.plist +```