Self-Hosting Backups and Recovery

Self-Hosting Backups and Recovery

A backup plan is only real after you have restored from it. The rest is optimism with a progress bar.

This guide is the practical version for a home server, small lab, or personal production stack: decide what matters, keep copies outside the blast radius, back up databases safely, protect the keys, and run small restore drills before a failure turns into a scavenger hunt.

If you are new to self-hosting, read this before you depend on anything important. If you already run services, use it as a recovery audit. A backup dashboard can say “success” while still leaving out the one database, token, or volume you needed.


The short version

  • Know what you are protecting. List the services, databases, files, secrets, and settings that matter.
  • Separate sync, snapshots, and backups. They solve different problems. Sync can also sync disasters.
  • Keep at least one copy outside the host. Local-only backups are convenient until the host, pool, account, or room is the thing that failed.
  • Keep history. Versioning matters because deletion, corruption, bad migrations, and ransomware are often discovered late.
  • Protect your recovery keys. Encrypted backups are good. Losing the only key is just data loss with better math.
  • Test restores on purpose. The restore test is where missing volumes, bad dumps, broken credentials, and undocumented steps reveal themselves.

The goal is not enterprise theater. The goal is a recovery path a tired human can follow without guessing.

Start with two questions

Before picking a backup tool, answer the boring questions. They decide the shape of the whole plan.

  • How much data can you afford to lose? If losing one day of changes is fine, daily backups may be enough. If losing an hour hurts, your plan needs shorter intervals or application-level exports.
  • How long can the service stay down? A backup that restores in six hours may be fine for a recipe app. It is not fine for password storage, client work, business files, or a service other people depend on.

Do this per service. A Plex library, a password vault, a WordPress site, a paperless document archive, and a home automation stack do not all deserve the same recovery plan.

Backup, sync, snapshot, replication: not the same thing

  • Sync keeps files in multiple places. It is useful for access and convenience, but a bad edit, deletion, or encrypted file can sync everywhere.
  • Snapshots are point-in-time rollback. They are excellent before updates, migrations, and risky changes, but they do not help if the disk, pool, host, or account disappears.
  • Replication copies state somewhere else. It helps availability, but it can also replicate broken state quickly and faithfully.
  • Backups are versioned recovery copies designed to survive ordinary failure, human mistakes, corruption, bad updates, and compromised systems.

Use all of them where they fit. Just do not let the words blur together. “It syncs to the NAS” is not the same claim as “I can restore last Tuesday’s working state after the app ate its database.”

The practical 3-2-1 shape

The classic 3-2-1 rule is useful because it makes you check failure domains: at least three copies, on two devices or media, with one offsite. For self-hosting, I would phrase it more plainly: keep the live data, keep a nearby recovery copy, and keep a separate copy that survives the obvious bad day.

  • Working copy: the live service, database, uploads, documents, media, or config.
  • Nearby recovery copy: local backup storage, a NAS, a second disk, a snapshot target, or another machine on the LAN.
  • Separate copy: cloud object storage, a backup provider, a disk stored elsewhere, or another trusted location.
  • Offline or immutable protection for critical data: something a compromised host cannot easily rewrite, delete, or encrypt.
  • Regular restore checks: the part that proves the plan is not just file collection.

For a throwaway test container, this can be simple. For family photos, password managers, WordPress content, client work, home automation, documents, or Plex metadata you care about, do not rely on a single machine with a brave little cron job.

What to back up

Most recovery failures come from missing supporting pieces, not from forgetting the obvious folder.

  • Service definitions: Docker Compose files, app manifests, systemd units, reverse proxy config, scheduled jobs, health checks, and update notes.
  • Application data: bind mounts, named Docker volumes, uploads, media folders, generated files, plugins, themes, custom templates, and app-specific storage directories.
  • Databases: PostgreSQL, MySQL, MariaDB, SQLite, Redis snapshots when needed, and any app database that contains users, state, history, requests, metadata, or settings.
  • Secrets: encryption keys, backup repository passwords, API tokens, recovery codes, license keys, SSH keys, DNS provider access, domain registrar access, and password manager recovery material.
  • Infrastructure notes: hostnames, IP addresses, ports, firewall rules, tunnel names, mount points, volume names, storage pools, dependencies, and the order services should come back.
  • Restore instructions: where the backup lives, what command or tool restores it, how to start the service, how to verify it worked, and what “done” looks like.

If a service cannot be rebuilt from a clean host plus your backup notes, the backup is incomplete.

Docker apps need two kinds of backups

A Docker image is not the application state. The useful recovery pieces usually live in Compose files, environment settings, bind mounts, named volumes, uploaded files, and databases.

  • Back up the recipe: Compose files, image tags, reverse proxy labels, app config, and a safe example of required environment variables.
  • Back up the state: named volumes, bind mounts, uploads, databases, and app storage paths.
  • Do not assume deleting a container deletes the data. Docker volumes can persist after containers are gone, which is useful only if you know they exist and how to restore them.
  • Use database-aware exports for live databases. A tarball of a running database volume may not be the clean restore point you think it is.

The Docker docs show volume backup and restore patterns using a helper container that mounts the volume and writes an archive. That is a useful building block, but database services still deserve proper database dumps or a clean stop before file-level backup.

Databases: make restorable exports

Database backups should be application-aware. Copying files underneath a running database can work in some carefully designed setups, but it is not the beginner default.

For small self-hosted services, logical exports are often the easiest recovery path:

# PostgreSQL: one database as a portable custom-format archive
pg_dump -Fc -d appdb -f appdb.dump
pg_restore --list appdb.dump

# MySQL or MariaDB: one InnoDB database as a logical dump
mysqldump --single-transaction --routines --triggers appdb > appdb.sql

# SQLite: use the SQLite backup path instead of copying a busy file
sqlite3 app.db ".backup 'app-backup.db'"

Those are patterns, not paste-and-pray instructions. Credentials, container names, socket paths, database names, permissions, and retention belong in your local runbook. The important part is that you know how to create the dump, inspect it enough to trust it, and restore it somewhere safe.

Secrets and keys are part of recovery

Encrypted backups are the right default for personal data, documents, databases, secrets, and anything you store with a third party. But encryption turns key management into part of the backup plan.

  • Keep backup repository passwords and encryption keys outside the server being backed up.
  • Store recovery codes and account access in a password manager or another deliberate secret store.
  • Document which key unlocks which backup target.
  • Make sure a trusted recovery path exists if the main machine and your usual browser session are both gone.
  • Do not put raw secrets in public repos, casual notes, screenshots, or shared docs.

A beautifully encrypted backup with a lost key is just a very private loss.

Retention: keep enough history to notice trouble late

The latest backup is not always the good one. Corruption, accidental deletion, bad imports, compromised files, and misconfigured sync jobs can sit unnoticed for days or weeks.

A reasonable starting point for important personal services:

  • Several recent daily copies for quick mistakes.
  • A few weekly copies for problems you notice late.
  • Some monthly copies for slow-moving damage or old reference needs.
  • Shorter retention for replaceable caches, downloads, and generated data.
  • Longer retention for documents, photos, project files, and important databases.

Retention is a tradeoff between storage cost and recovery options. Choose it intentionally, then monitor whether the storage target is quietly filling up.

The 30-minute restore drill

Do not start with a full disaster-recovery opera. Pick one service and prove the path works.

  1. Pick a service that matters enough to test.
  2. Find the latest backup and confirm when it ran.
  3. Restore into a safe target: a temporary folder, spare VM, staging container, or test host.
  4. Restore the database if the app uses one.
  5. Restore uploads, media, config, secrets, and mounted volumes as needed.
  6. Start the service without pointing production users at it.
  7. Verify real data, not just a login page.
  8. Measure how long it took and what was confusing.
  9. Fix the backup job or notes while the pain is fresh.

Quarterly is a good cadence for important personal systems. Monthly is not silly for critical services. “Never” is also a cadence, technically, but it is the wrong one.

A recovery note should answer these questions

  • What service is this?
  • Where does its live data live?
  • Which files, volumes, databases, and secrets are required?
  • Where are the backups stored?
  • How often do they run?
  • How far back does retention go?
  • What unlocks or authenticates the backup target?
  • How do you restore it to a test location?
  • How do you verify the app is actually healthy?
  • What was the date of the last successful restore test?

Plain Markdown is enough. The winning format is the one you can read under pressure.

Common self-hosting backup failures

  • The database was not included. The app files restore, but the actual state is gone.
  • The backup only lived on the same host. Disk failure, theft, fire, account loss, ransomware, or a bad command takes both copies.
  • The job failed silently. A cron job, container, token, mount, or target broke weeks ago.
  • The restore key was on the dead machine. Encryption worked. Recovery did not.
  • Retention was too short. The backup faithfully preserved the already-broken version.
  • The restore steps were never written down. Future-you had to reconstruct the system from memory and vibes.
  • The backup target was always online and writable. An attacker or bad script could delete or encrypt it along with production.

A starter plan that is good enough to improve

For one important self-hosted app, aim for this first:

  • Back up the Compose file, config, mounted data, uploads, and database dump.
  • Send a copy to local backup storage for quick restores.
  • Send a second copy to a separate/offsite target.
  • Encrypt the backup before it leaves your control.
  • Keep versions long enough to recover from delayed discoveries.
  • Alert when a backup job fails or the target stops receiving new data.
  • Restore to a test location once, then write down the exact recovery path.

After that works for one service, repeat the pattern. Boring and repeated beats clever and undocumented.

Sources and further reading

Related CyganLabs paths

Best next step

Pick one service this week and run a small restore drill. If you are not sure where to start, use Backup Reality Check, then write down the missing pieces. The notes you make during a calm test are the notes you will wish existed during a loud failure.

Scroll to Top