My backup strategy has been evolving. Here’s where it is today: automated, encrypted, and separated by concern.
The setup backs up my Arch Linux workstation to TrueNAS running MinIO (S3-compatible). Home directory and system files go to separate repositories. Credentials are TPM2-encrypted via systemd-creds. Everything runs on timers.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| ┌──────────────────────┐
│ Linux Workstation │
│ (Arch) │
├──────────────────────┤
│ ┌──────────────────┐ │
│ │ HOME BACKUP │ │ ┌─────────────────────────┐
│ │ /home/amlucas │─┼────▶│ TrueNAS + MinIO │
│ └──────────────────┘ │ │ <truenas-address>:9000 │
│ │ │ │
│ ┌──────────────────┐ │ │ ┌─────────────────────┐ │
│ │ SYSTEM BACKUP │ │ │ │ home-amlucas-backup │ │
│ │ / (excluding │─┼────▶│ └─────────────────────┘ │
│ │ /home) │ │ │ │
│ └──────────────────┘ │ │ ┌─────────────────────┐ │
└──────────────────────┘ │ │ slash-amlucas-backup│ │
│ └─────────────────────┘ │
└─────────────────────────┘
|
Credential Security
Instead of storing passwords in plain text, I use systemd-creds with TPM2 hardware encryption. First verify TPM2 is available:
1
2
| systemd-analyze has-tpm2
# Should return: yes +firmware +driver +system +subsystem +libraries
|
Then encrypt the credentials:
1
2
3
4
5
6
7
| sudo mkdir -p /etc/systemd/creds
# Add space before command - won't be logged in history
echo "your-aws-access-key" | sudo systemd-creds encrypt --name=aws-access-key-id - /etc/systemd/creds/aws-access-key-id.cred
echo "your-aws-secret-key" | sudo systemd-creds encrypt --name=aws-secret-access-key - /etc/systemd/creds/aws-secret-access-key.cred
echo "your-home-password" | sudo systemd-creds encrypt --name=restic-home-password - /etc/systemd/creds/restic-home-password.cred
echo "your-system-password" | sudo systemd-creds encrypt --name=restic-system-password - /etc/systemd/creds/restic-system-password.cred
|
Backup Scripts
I separate home and system backups. The home backup script reads credentials from systemd and runs restic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| #!/bin/bash
set -e
if [[ -n "${CREDENTIALS_DIRECTORY}" ]]; then
export AWS_ACCESS_KEY_ID="$(cat "${CREDENTIALS_DIRECTORY}/aws-access-key-id")"
export AWS_SECRET_ACCESS_KEY="$(cat "${CREDENTIALS_DIRECTORY}/aws-secret-access-key")"
export RESTIC_PASSWORD="$(cat "${CREDENTIALS_DIRECTORY}/restic-home-password")"
else
echo "ERROR: Must run via systemd service."; exit 1
fi
export RESTIC_REPOSITORY='s3:http://<truenas-address>:9000/home-amlucas-backup'
restic backup /home/amlucas --exclude-file=/home/amlucas/.restic-excludes --exclude-caches --tag daily
restic forget --keep-daily 7 --keep-weekly 4 --prune
|
The system backup is identical except it backs up / with --exclude /home/* to prevent duplication.
Exclusion files filter out caches, node_modules, downloads, and other regenerable content. The system exclusion critically includes /home/* to avoid backing up home directory twice.
Systemd Automation
Each backup has a service and timer. The service loads encrypted credentials:
1
2
3
4
5
6
7
8
9
10
11
| [Unit]
Description=Restic backup
After=network-online.target
[Service]
Type=oneshot
User=amlucas
LoadCredentialEncrypted=aws-access-key-id:/etc/systemd/creds/aws-access-key-id.cred
LoadCredentialEncrypted=aws-secret-access-key:/etc/systemd/creds/aws-secret-access-key.cred
LoadCredentialEncrypted=restic-home-password:/etc/systemd/creds/restic-home-password.cred
ExecStart=/home/amlucas/opt/code/autostart/backup.sh
|
The timer runs daily with persistence (catches up if machine was off):
1
2
3
4
5
6
7
8
9
10
| [Unit]
Description=Run Restic backup daily
Requires=restic-backup.service
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
|
Enable both timers:
1
2
3
| sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
sudo systemctl enable --now restic-slash-backup.timer
|
Recovery
To restore, export credentials and use restic commands:
1
2
3
4
5
6
7
8
| export AWS_ACCESS_KEY_ID="your-aws-access-key"
export AWS_SECRET_ACCESS_KEY='your-aws-secret-key'
export RESTIC_REPOSITORY='s3:http://<truenas-address>:9000/home-amlucas-backup'
export RESTIC_PASSWORD='your-restic-password'
restic snapshots # List snapshots
restic mount /mnt/restic # Browse interactively
restic restore latest --target /tmp/restore --include /home/amlucas/Documents # Restore specific path
|
Monitor with systemctl list-timers and journalctl -u restic-backup.service.
Important: Disaster recovery still requires the original passwords. Test recovery procedures regularly.
This works for my Linux-to-TrueNAS setup. Adjust the encryption method, retention policy, and exclusions for yours.