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.