Featured image of post How I Handle Backups: Linux to TrueNAS with Restic

How I Handle Backups: Linux to TrueNAS with Restic

My backup strategy using Restic, MinIO S3 storage, and TPM2-encrypted credentials for automated Linux backups to TrueNAS.

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.

uname -a: Human 5.4.0-anxiety #1 SMP Coffee-Deprived x86_64 GNU/Paranoid
Built with Hugo
Theme Stack designed by Jimmy