Skip to main content
Back to blog

Backup strategies for self-hosted data

·3 min readSelf-Hosting

If you self-host services, your data is your responsibility. No cloud provider is backing it up for you. If your drive fails, your Docker volume gets corrupted, or you accidentally run rm -rf in the wrong directory, your data is gone unless you have backups.

The stakes are real. I am talking about your password vault, your photos, your files, your databases. Losing any of these would be a bad day.

The 3-2-1 rule

Keep at least 3 copies of your data, on 2 different types of media, with 1 copy offsite. For a homelab this translates to:

  1. Primary data on your server (the live copy)
  2. Local backup on a different drive or NAS (protects against drive failure)
  3. Offsite backup at a different physical location (protects against fire, theft, hardware failure)
graph LR
    P[🖥 Server<br/>Primary Data] -->|Copy 1| L[💾 NAS / Second Drive<br/>Local Backup]
    P -->|Copy 2| O[☁ Offsite<br/>Remote / Cloud]
    style P fill:#1e3a5f,stroke:#3b82f6,color:#fff
    style L fill:#1e3a2f,stroke:#22c55e,color:#fff
    style O fill:#3a1e3f,stroke:#a855f7,color:#fff

What to back up

Not everything needs the same backup strategy:

Critical (daily backups, offsite): Password vault database, important documents, photos, financial records.

Important (daily backups, local): Docker volumes for services you actively use, configuration files, databases.

Nice to have (weekly, local only): Media libraries, cached data, things you can re-download.

Tools I use

Restic for encrypted, deduplicated backups. It supports local drives, SFTP, S3, and many cloud backends:

# Initialize a backup repository
restic init --repo /mnt/backup/restic-repo
 
# Back up a directory
restic backup /path/to/important/data
 
# Back up with exclusions
restic backup /home/user --exclude=node_modules --exclude=.cache
 
# List snapshots
restic snapshots
 
# Restore from a snapshot
restic restore latest --target /tmp/restore

Restic deduplicates data across backups, so incremental backups are fast and space-efficient. A 100GB dataset with daily backups might only use 110GB of backup space after a month.

Cron for scheduling:

# /etc/cron.d/backup
0 3 * * * root restic backup /data --repo /mnt/backup/restic-repo --password-file /root/.restic-password
0 4 * * 0 root restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --repo /mnt/backup/restic-repo --password-file /root/.restic-password

This runs a backup at 3 AM daily and cleans up old snapshots weekly, keeping 7 daily, 4 weekly, and 6 monthly snapshots.

Database backups

Docker volumes are not the best way to back up databases. A volume backup captures the database files mid-write, which can result in corruption. Use the database's native dump tool instead:

# PostgreSQL
docker exec postgres pg_dump -U user dbname > backup.sql
 
# SQLite (Vaultwarden, etc.)
sqlite3 /data/db.sqlite3 ".backup '/backup/db-backup.sqlite3'"

Run these before your Restic backup so the dump files are included.

Offsite backup

For offsite, I use Restic with an S3-compatible backend (Backblaze B2 at $0.005/GB/month):

export AWS_ACCESS_KEY_ID=your-key
export AWS_SECRET_ACCESS_KEY=your-secret
restic backup /data --repo s3:s3.us-west-001.backblazeb2.com/my-backup-bucket

An alternative is replicating ZFS snapshots to a friend's TrueNAS server over SSH. Free, encrypted in transit, and you both benefit.

Test your restores

A backup you have never restored from is not a backup. It is a hope. Schedule quarterly restore tests:

  1. Pick a backup
  2. Restore it to a temporary location
  3. Verify the data is intact
  4. Delete the temporary restore

I learned this the hard way when a backup I relied on turned out to have permission issues that made the restored files unreadable.

Sources

Enjoying the blog? Subscribe via RSS to get new posts in your reader.

Subscribe via RSS