How I handle backups on my Linux machine to TrueNAS

This guide sets up secure, automated backups from Linux to TrueNAS using Restic with MinIO S3 storage and TPM2 encryption

I needed a robust, automated backup solution for my Linux machine:

  • Backs up to my TrueNAS server running MinIO (S3-compatible)
  • Separates system files from personal data
  • Runs automatically
  • Handles encryption and deduplication
  • Provides easy restoration capabilities
  • Secure credential storage (using systemd-creds with TPM2 hw encryption)

Architecture Overview

 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│ │
                             │ └─────────────────────┘ │
                             └─────────────────────────┘

Initial Setup

Prerequisites

1
2
3
4
# Install Restic
sudo pacman -S restic  # Arch Linux
# or
sudo apt install restic  # Debian/Ubuntu

MinIO Configuration

My TrueNAS server runs MinIO on:

  • API Port: 9000 (for S3 operations)
  • Console Port: 9001/9002 (web interface)

Secure Credential Storage with systemd-creds

Security Enhancement: Instead of storing passwords in plain text, this setup uses systemd-creds with TPM2 hardware encryption for maximum security.

Encrypted Credential Files

1
2
3
4
5
# Created at /etc/systemd/creds/
aws-access-key-id.cred           # Encrypted MinIO access key
aws-secret-access-key.cred       # Encrypted MinIO secret key  
restic-home-password.cred        # Encrypted password for HOME repository
restic-system-password.cred      # Encrypted password for SYSTEM repository

Setup systemd-creds Security

1. Verify TPM2 Availability

1
2
3
# Check if TPM2 is available
systemd-analyze has-tpm2
# Should return: yes +firmware +driver +system +subsystem +libraries

2. Create Encrypted Credentials

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Create credentials directory
sudo mkdir -p /etc/systemd/creds

# Encrypt credentials (replace with your actual values)
# Add a space before the 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 credential files: `sudo cp -r /etc/systemd/creds ~/systemd-creds-backup`

3. Initialize Repositories

Directory Structure

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
~/
├── opt/code/autostart/
│   ├── backup.sh          # HOME backup script
│   └── backup-slash.sh    # SYSTEM backup script
├── .restic-excludes       # HOME exclusions
└── .restic-excludes-slash # SYSTEM exclusions

/etc/systemd/system/
├── restic-backup.service
├── restic-backup.timer
├── restic-slash-backup.service
└── restic-slash-backup.timer

Config Files

HOME Exclusion List (~/.restic-excludes)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
**/.cache/
**/node_modules/
**/.npm/
**/.yarn/
**/.pnpm-store/
**/Downloads/
*~
*.tmp
*.temp
**/.local/share/Steam/
**/.mozilla/firefox/*/storage/
**/.config/google-chrome/*/History*

SYSTEM Exclusion List (~/.restic-excludes-slash)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/dev/*
/proc/*
/sys/*
/tmp/*
/run/*
/mnt/*
/media/*
/lost+found
/var/cache/*
/var/tmp/*
/var/log/*
/home/*    # CRITICAL: Prevent duplication!
**/node_modules/
**/.npm/
**/.yarn/
**/.pnpm-store/
**/Downloads/
*~
*.tmp
*.temp

Scripts

HOME Backup Script (~/opt/code/autostart/backup.sh)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash
set -e

# Read credentials from systemd-creds (loaded by systemd service)
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: CREDENTIALS_DIRECTORY not set. This script must be run via systemd service."
    exit 1
fi

export RESTIC_REPOSITORY='s3:http://<truenas-address>:9000/home-amlucas-backup'

echo "Starting backup..."
restic backup /home/amlucas \
  --exclude-file=/home/amlucas/.restic-excludes \
  --exclude-caches \
  --tag daily

echo "Cleaning up old backups..."
restic forget --keep-daily 7 --keep-weekly 4 --prune

echo "Backup completed successfully!"

SYSTEM Backup Script (~/opt/code/autostart/backup-slash.sh)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash
set -e

# Read credentials from systemd-creds (loaded by systemd service)
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-system-password")"
else
    echo "ERROR: CREDENTIALS_DIRECTORY not set. This script must be run via systemd service."
    exit 1
fi

export RESTIC_REPOSITORY="s3:http://<truenas-address>:9000/slash-amlucas-backup"

echo "Starting system backup..."
restic backup / \
  --exclude-file=/home/amlucas/.restic-excludes-slash \
  --exclude-caches \
  --tag system

echo "Cleaning up old system backups..."
restic forget --keep-daily 7 --keep-weekly 4 --prune

echo "System backup completed successfully!"

Make Scripts Executable

1
2
chmod +x ~/opt/code/autostart/backup.sh
chmod +x ~/opt/code/autostart/backup-slash.sh

Systemd Automation

HOME Backup Service (/etc/systemd/system/restic-backup.service)

 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

HOME Backup Timer (/etc/systemd/system/restic-backup.timer)

 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

SYSTEM Backup Service (/etc/systemd/system/restic-slash-backup.service)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[Unit]
Description=Restic system 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-system-password:/etc/systemd/creds/restic-system-password.cred
ExecStart=/home/amlucas/opt/code/autostart/backup-slash.sh

SYSTEM Backup Timer (/etc/systemd/system/restic-slash-backup.timer)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[Unit]
Description=Run Restic system backup daily
Requires=restic-slash-backup.service

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

Enable and Start Services

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Reload systemd daemon
sudo systemctl daemon-reload

# Enable and start HOME backup timer
sudo systemctl enable restic-backup.timer
sudo systemctl start restic-backup.timer

# Enable and start SYSTEM backup timer
sudo systemctl enable restic-slash-backup.timer
sudo systemctl start restic-slash-backup.timer

# Check status
systemctl status restic-backup.timer
systemctl status restic-slash-backup.timer

Testing and Verification

Manual Test Run

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Test HOME backup via systemd
sudo systemctl start restic-backup.service

# Test SYSTEM backup via systemd
sudo systemctl start restic-slash-backup.service

# Check status after running
sudo systemctl status restic-backup.service
sudo systemctl status restic-slash-backup.service

# View logs if needed
sudo journalctl -u restic-backup.service
sudo journalctl -u restic-slash-backup.servic

Check Backup Status

1
2
3
4
5
6
7
8
9
# For HOME repository
export RESTIC_REPOSITORY='s3:http://<truenas-address>:9000/home-amlucas-backup'
 export RESTIC_PASSWORD='your-home-password'
restic snapshots

# For SYSTEM repository  
export RESTIC_REPOSITORY="s3:http://<truenas-address>:9000/slash-amlucas-backup"
 export RESTIC_PASSWORD='your-system-password'
restic snapshots

Recovery Procedures

Restore

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# List available snapshots

#sudo systemd-run -P --wait \
#    -p LoadCredentialEncrypted=aws-access-key-id:/etc/systemd/creds/aws-access-key-id.cred \
#    -p LoadCredentialEncrypted=aws-secret-access-key:/etc/systemd/creds/aws-secret-access-key.cred \
#    -p LoadCredentialEncrypted=restic-home-password:/etc/systemd/creds/restic-home-password.cred \
#    bash -c 'export AWS_ACCESS_KEY_ID=$(cat $CREDENTIALS_DIRECTORY/aws-access-key-id) \
#             AWS_SECRET_ACCESS_KEY=$(cat $CREDENTIALS_DIRECTORY/aws-secret-access-key) \
#             RESTIC_PASSWORD=$(cat $CREDENTIALS_DIRECTORY/restic-home-password) \
#             RESTIC_REPOSITORY=s3:http://<truenas-address>:9000/home-amlucas-backup; \
#             restic snapshots' # <-- restic command
# or

 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-key'

# List snapshots
restic snapshots

# Browse snapshot interactively
restic mount /mnt/restic

# Restore specific file/directory
restic restore latest --target /tmp/restore --include /home/amlucas/Documents

Monitoring

Check Timer Status

1
2
3
4
5
6
# List all timers
systemctl list-timers

# Check specific timer logs
journalctl -u restic-backup.timer
journalctl -u restic-backup.service

Important: Disaster recovery still requires the original passwords. Test recovery procedures regularly.


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