commit db1e0de47d06e54ff4b851853c9db508b8fa09b4
parent 3aa3c98fa82dd90ae0e7d5454e7dfe32b50ef8a6
Author: Julian Piribauer <julian.piribauer@gmail.com>
Date: Thu, 14 May 2026 12:54:15 +0000
Adding restic doc
Diffstat:
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
+```