Quick answer

What this guide helps you do

A simple home server backup strategy built around 3-2-1, automatic copies, offsite protection, and tested restores.

Difficulty

Beginner-friendly

Focus

Small home server setup

Best used for

Practical setup, fixes, and checks

Quick answer

A practical 3-2-1 home-server backup plan is:

Primary copy: live data on the server
Second copy: automatic local backup on another disk or machine
Third copy: encrypted cloud backup or rotated disk stored elsewhere
Proof: regular restore tests

The target is not three folders on the same disk. The copies must fail differently.

If you have never restored from the backup, treat it as unverified.


What this guide covers

This guide focuses on recoverable backups for a small Linux home server.

It covers:

  • deciding what deserves backup protection
  • separating live data from backup copies
  • creating a local rsync backup
  • adding encrypted offsite protection with restic
  • retention and pruning
  • logging and failure detection
  • testing local and offsite restores
  • documenting the recovery process

Snapshots are useful before risky changes, but they are not a replacement for separate backup copies.


SmallGrid verification method

SmallGrid treats a backup as verified only when all four layers have been checked:

  1. the source data is known
  2. the backup job finishes and logs its result
  3. the destination contains the expected files or snapshots
  4. a selected file or folder can be restored to a temporary location and opened

The examples use generic paths so they can be adapted safely. Do not copy device names such as /dev/sdX without checking your own system.


Backup decision table

Data typePrioritySuggested protectionRestore test
Photos and personal documentsCriticalLocal automatic backup plus encrypted offsite copyRestore a representative folder quarterly
Docker Compose, environment files and scriptsCriticalVersioned local and offsite backupRebuild one service in a test location
Application databases and configurationHighConsistent scheduled backup with retentionRestore into a temporary test instance
Proxmox VM backupsHighSeparate storage plus offsite copy where practicalRestore one VM or configuration periodically
Jellyfin metadata and watch stateMediumLocal scheduled backupRestore the Jellyfin config directory in a test copy
Replaceable mediaLow to mediumProtect only when re-creating it would be costlyRestore a sample if included
Cache, downloads and temporary filesLowUsually excludeNo restore test required

SmallGrid rule: back up what hurts to lose first.


Understand 3-2-1 properly

The classic rule is:

  • 3 copies of important data
  • 2 different storage systems or failure domains
  • 1 copy away from the main server

A poor example is:

/srv/data
/srv/data-backup
/srv/data-copy

when all three directories are on the same physical disk.

A better example is:

Live data:      internal server storage
Local backup:   separate USB disk or another machine
Offsite backup: encrypted cloud repository or rotated disk elsewhere

Step 1: Inventory the data

Before choosing tools, identify the source paths:

sudo du -sh /srv/* 2>/dev/null
sudo find /srv -maxdepth 3 -type f | head -50

Create a simple inventory:

SourceContainsPriorityBackup target
/srv/datadocuments and projectsCriticallocal plus offsite
/srv/dockerCompose and service configCriticallocal plus offsite
/var/lib/jellyfinmetadata and watch stateMediumlocal backup
/srv/mediareplaceable mediaDependsselective or no backup

Do not start with the largest directory. Start with the data that cannot be recreated.


Step 2: Prepare a separate local destination

Identify the backup disk:

lsblk -o NAME,SIZE,FSTYPE,MODEL,MOUNTPOINTS
blkid

Be careful: formatting the wrong disk destroys data.

Create a stable mount point:

sudo mkdir -p /mnt/backup

For a permanently attached disk, mount by UUID in /etc/fstab rather than relying on /dev/sdX remaining stable.

Validate the mount:

sudo mount -a
findmnt /mnt/backup

Do not run a destructive or --delete backup until findmnt confirms the expected destination is mounted.


Step 3: Create a safer local backup script

Create the script:

sudo nano /usr/local/sbin/backup-data.sh

Use:

#!/usr/bin/env bash
set -euo pipefail

SRC="/srv/data/"
DEST="/mnt/backup/data/"
LOG="/var/log/backup-data.log"

stamp() {
  date +"%Y-%m-%d %H:%M:%S"
}

if ! findmnt -M /mnt/backup >/dev/null 2>&1; then
  echo "$(stamp) - ERROR: /mnt/backup is not mounted" | tee -a "$LOG"
  exit 1
fi

mkdir -p "$DEST"

echo "$(stamp) - Starting local backup" | tee -a "$LOG"

rsync -aHAX --delete \
  --numeric-ids \
  --exclude=".cache/" \
  --exclude="*.tmp" \
  "$SRC" "$DEST" | tee -a "$LOG"

echo "$(stamp) - Local backup finished" | tee -a "$LOG"

Make it executable:

sudo chmod 750 /usr/local/sbin/backup-data.sh

The mount check prevents rsync --delete from writing into an empty mount-point directory when the backup disk is absent.


Step 4: Test the local backup manually

Run it once:

sudo /usr/local/sbin/backup-data.sh

Check the destination and log:

findmnt /mnt/backup
sudo find /mnt/backup/data -maxdepth 3 -type f | head -20
sudo tail -n 50 /var/log/backup-data.log

Compare source and destination totals:

sudo du -sh /srv/data /mnt/backup/data

The totals do not need to be byte-for-byte identical in every filesystem, but a large unexplained difference needs investigation.


Step 5: Schedule the backup

A systemd timer provides clearer status than a silent cron job.

Create the service:

sudo nano /etc/systemd/system/backup-data.service
[Unit]
Description=Back up important server data
RequiresMountsFor=/mnt/backup

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/backup-data.sh

Create the timer:

sudo nano /etc/systemd/system/backup-data.timer
[Unit]
Description=Run the local data backup nightly

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=10m

[Install]
WantedBy=timers.target

Enable it:

sudo systemctl daemon-reload
sudo systemctl enable --now backup-data.timer
systemctl list-timers backup-data.timer

Check a completed run with:

systemctl status backup-data.service --no-pager
journalctl -u backup-data.service --no-pager -n 100

Step 6: Add an encrypted offsite copy

Offsite protection covers failures that affect the server and local backup together.

Practical options include:

  • encrypted object storage using restic
  • another trusted machine in a different location
  • a rotated USB disk stored elsewhere
  • a dedicated backup provider

This example uses restic because it provides encryption, snapshots, retention, and restore tools.

Install it:

sudo apt update
sudo apt install -y restic

Store repository credentials in a protected environment file:

sudo nano /root/.restic-env
sudo chmod 600 /root/.restic-env

Example variables:

export RESTIC_REPOSITORY="s3:https://YOUR-ENDPOINT/YOUR-BUCKET"
export RESTIC_PASSWORD_FILE="/root/.restic-password"
export AWS_ACCESS_KEY_ID="YOUR-KEY"
export AWS_SECRET_ACCESS_KEY="YOUR-SECRET"

Store the repository password in /root/.restic-password with mode 600. Keep a protected copy of that password away from the server. An encrypted repository without its password is unrecoverable.

Initialise the repository:

sudo -i
source /root/.restic-env
restic init
exit

Step 7: Back up and retain snapshots

Create the offsite script:

sudo nano /usr/local/sbin/backup-offsite.sh
#!/usr/bin/env bash
set -euo pipefail

source /root/.restic-env
LOG="/var/log/backup-offsite.log"

restic backup /srv/data /srv/docker \
  --tag smallgrid-server | tee -a "$LOG"

restic forget \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 6 \
  --prune | tee -a "$LOG"

restic check | tee -a "$LOG"

Make it executable and test it manually:

sudo chmod 750 /usr/local/sbin/backup-offsite.sh
sudo /usr/local/sbin/backup-offsite.sh

List snapshots:

sudo -i
source /root/.restic-env
restic snapshots
exit

Run offsite backups at a frequency that matches how often the protected data changes.


Step 8: Test a local restore

Create a temporary restore location:

sudo mkdir -p /tmp/local-restore-test
sudo rsync -aHAX /mnt/backup/data/projects/example/ /tmp/local-restore-test/

Inspect and open the restored data:

sudo find /tmp/local-restore-test -maxdepth 3 -type f
sudo stat /tmp/local-restore-test/*

Check:

  • the expected files exist
  • permissions and ownership are sensible
  • files can be opened
  • an application can read the restored data where relevant

Delete only the temporary restore copy when finished.


Step 9: Test an offsite restore

List snapshots and restore a small path:

sudo -i
source /root/.restic-env
restic snapshots
restic restore latest \
  --target /tmp/restic-restore-test \
  --include "/srv/data/projects/example"
exit

Inspect the result:

sudo find /tmp/restic-restore-test -maxdepth 5 -type f

A successful restic check is useful, but it does not replace restoring and opening actual files.


Worked verification record

Record restore tests in a simple table:

DateBackup sourceRestored itemDestinationResultNotes
2026-07-09local USB backuptest project folder/tmp/local-restore-testPass or failRecord permissions and application check
2026-07-09offsite restic snapshotsame test folder/tmp/restic-restore-testPass or failRecord snapshot ID and errors

Use your real date and result. Do not mark a test as passed until the restored files have been opened or used.


Failure scenarios to test

FailureExpected recovery source
Main data disk failsLocal or offsite backup
Accidental deletionVersioned offsite snapshot or retained local copy
Local backup disk failsOffsite copy
Server is stolen or damagedOffsite copy plus documented rebuild steps
Bad configuration deploymentConfiguration backup or tested snapshot
Backup job silently stopsMonitoring, logs, and regular restore review
Ransomware or destructive command affects mounted storageOffline, immutable, or separately protected offsite copy

A permanently attached local disk is convenient, but it may share some risks with the server. That is why the offsite copy matters.


Final verification checklist

  • Important source paths are documented.
  • The local destination is a separate disk or machine.
  • The backup script refuses to run when the destination is not mounted.
  • Automatic runs have visible status and logs.
  • At least one copy is away from the main server.
  • Offsite backups are encrypted.
  • The encryption password is stored separately and securely.
  • Retention rules keep more than only the latest state.
  • A local file or folder has been restored and opened.
  • An offsite file or folder has been restored and opened.
  • The recovery steps are documented somewhere outside the server.
  • Restore tests are repeated on a schedule.

Common mistakes

Copying to the same physical disk

A second directory is not protection from disk failure.

Using --delete without checking the mount

An absent destination disk can turn the mount point into a normal empty directory. Always verify the mount first.

Backing up live databases inconsistently

Some applications need database dumps, application-aware exports, or a short service stop to produce a consistent backup.

Keeping only one current copy

Mirrors reproduce deletion and corruption. Versioned snapshots provide a route back to an earlier state.

Never checking logs

A backup job that silently fails creates false confidence.

Never testing restore

A restore test is the difference between a backup system and a collection of unproven files.

Confusing snapshots with backups

Snapshots are useful for rollback, but they usually share storage and failure risks with the original data.

Read Proxmox Snapshots vs Backups: What Beginners Get Wrong.



Recap

A practical 3-2-1 backup system has live data, a separate local copy, an offsite copy, version history, visible logs, and tested restores.

Start with the data that cannot be recreated. Automate the local copy, add encrypted offsite protection, and regularly restore a representative folder to prove the system still works.