← Learn··Updated 31 May 2026·8 min read

Homelab storage and backups

Add a data disk to your Debian homelab the right way: partition it, format it, and mount it persistently by UUID in /etc/fstab. Then back up your Docker volumes with restic following the 3-2-1 rule, and actually test a restore.

Homelab
Build a homelab on DebianPart 6 of 8
#homelab
#backups
#ai-assisted

level:Intermediate verified:Jun 2026

ℹ️ Hands-on — you'll create files and run commands on your own Debian box.

Your homelab is running services now, and those services are writing data: a database here, uploaded files there, config you spent an evening getting right. That data lives on whatever disk Debian was installed on, and right now you have exactly one copy of it. A failed disk, a fat-fingered rm, or a botched upgrade and it is gone. This post does two related jobs. First, it adds a dedicated data disk — partition, format, and mount it so it survives a reboot. Then it sets up backups you can trust, following the 3-2-1 rule and, crucially, testing that a restore actually works. None of this is hard, but a couple of the commands are genuinely destructive, so we go slowly.

Adding a data disk

Keeping your data on a separate disk from the operating system is a good habit. The OS disk can be wiped and reinstalled without touching your data, and a data disk can be grown or moved independently. The work has four steps: partition the disk, put a filesystem on it, mount it, and make that mount persist across reboots.

Find the disk

When you add a physical or virtual disk, Debian gives it a device name like /dev/sdb or /dev/vdb. List your block devices to see what is there — run this on your Debian box:

lsblk

You get every disk and its partitions in a tree, with sizes. A brand-new disk shows up with no partitions under it — that is how you tell it apart from your OS disk, which already has partitions mounted at / and /boot. Confirm the size matches the disk you just added so you do not target the wrong device (oneuptime: lsblk, blkid, and mount).

Danger Every command below operates on a whole disk. If you point parted, mkfs, or wipefs at your OS disk by mistake, you destroy your running system. Run lsblk first, confirm the new disk is empty and the size is what you expect, and double-check the device name in every command. There is no undo.

Partition it

For a data disk you want a GPT partition table and a single partition filling the whole disk. The mechanics of partition tables are covered in the partitioning explainer; here we just do it with parted. Run both commands on your Debian box:

sudo parted /dev/sdb --script mklabel gpt
sudo parted /dev/sdb --script mkpart primary ext4 0% 100%

The first command writes a GPT label; the second creates one partition spanning the disk. Run lsblk again and you should now see /dev/sdb1 under /dev/sdb.

Put a filesystem on it

A partition is just a boundary — it cannot hold files until you create a filesystem inside it. ext4 is the sensible default for a homelab data disk; the trade-offs between filesystems are in the filesystems explainer. Format the partition, not the disk — run this on your Debian box:

sudo mkfs.ext4 /dev/sdb1

It prints the filesystem details it wrote (block count, the new UUID) and returns to the prompt.

⚠️ Warning Note the 1 — you format /dev/sdb1, the partition, not /dev/sdb, the whole disk. Running mkfs against a disk that already holds data overwrites it instantly with no prompt. Read the device name out loud before you press Enter.

Mount it once, by hand

Create a mount point and mount the partition there to confirm it works — run these on your Debian box:

sudo mkdir -p /srv/data
sudo mount /dev/sdb1 /srv/data
df -h /srv/data

df -h should now show /dev/sdb1 mounted at /srv/data with the expected free space. This mount is temporary — it disappears on the next reboot. To make it permanent you add it to /etc/fstab.

What /etc/fstab is

/etc/fstab (the "file systems table") is the configuration file Debian reads at boot to decide which filesystems to mount, where, and how. Each non-comment line describes one mount and has six whitespace-separated fields (Debian Wiki: fstab):

  1. what to mount — the device, given as a UUID rather than /dev/sdb1 (more on why below)
  2. where to mount it — the mount point, e.g. /srv/data
  3. filesystem typeext4 here
  4. optionsdefaults is the standard set (read-write, auto-mount at boot, and the usual flags)
  5. dump — used by the old dump backup tool; 0 means "ignore", which is what you want
  6. pass — fsck check order at boot; 1 is reserved for the root filesystem, so a data partition gets 2
flowchart LR
  A["/etc/fstab line"] --> B["UUID=...<br/><i>what</i>"]
  A --> C["/srv/data<br/><i>where</i>"]
  A --> D["ext4<br/><i>type</i>"]
  A --> E["defaults<br/><i>options</i>"]
  A --> F["0 2<br/><i>dump &amp; fsck pass</i>"]

The six fstab fields, left to right.

Why UUID, not /dev/sdb1

Device names like /dev/sdb are assigned in the order the kernel finds disks at boot, and that order is not guaranteed. Add another disk, or have one enumerate slightly slower one morning, and yesterday's /dev/sdb can become today's /dev/sdc. If /etc/fstab names the device by path, the wrong filesystem mounts at /srv/data — or none does, and the boot stalls. A UUID is a unique identifier baked into the filesystem itself when you ran mkfs, so it follows the data no matter how the kernel names the disk (nixCraft: using UUIDs in fstab). Always mount by UUID.

Read the UUID with blkid — run this on your Debian box:

sudo blkid /dev/sdb1

That prints a line including UUID="xxxxxxxx-xxxx-...". Copy the value between the quotes.

Persist it in /etc/fstab

Open /etc/fstab in an editor as root (sudoedit /etc/fstab) and add a line at the end, substituting your UUID. This is a file edit, not a command you run:

/etc/fstab

UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  /srv/data  ext4  defaults  0  2
Danger Use an editor (sudoedit /etc/fstab), not shell redirection. A single > where you meant >> truncates the entire file, wiping every existing mount including root, and the system may not boot. If you must append from the shell, it is >>, and you should still open the file afterward to confirm nothing was clobbered.

Before rebooting, verify the file is sane. Debian ships a checker exactly for this — run it on your Debian box:

sudo findmnt --verify --verbose

It parses /etc/fstab and flags bad UUIDs, missing mount points, and syntax errors — catching the mistakes that would otherwise only surface as a failed boot (Debian Wiki: fstab). Then test the new line without rebooting — run these on your Debian box:

sudo umount /srv/data
sudo mount -a
df -h /srv/data

mount -a mounts everything in /etc/fstab; if your new line is wrong it errors here, on a running system you can fix, instead of at boot. Once df -h shows /srv/data mounted, point your Docker volumes and configs at it and move on.

Backups that actually work

A data disk gives you one tidy copy of your data. One copy is not a backup — it is a single point of failure waiting for a disk to die or a command to go wrong. This is where the 3-2-1 rule comes in.

The 3-2-1 rule

The 3-2-1 rule is the long-standing baseline for not losing data: keep 3 copies of your data, on 2 different types of media, with 1 copy off-site (DoHost: the 3-2-1 rule revisited). For a homelab that means: your live data on /srv/data is copy one; a backup repository on a second disk or a NAS is copy two, on different media; and a copy pushed off-site — a cloud bucket, a friend's box, an external drive you rotate — is copy three. The off-site copy is the one that saves you from fire, theft, or ransomware that reaches everything plugged into the same machine.

flowchart TD
  A["Live data<br/><i>/srv/data — copy 1</i>"] --> B["Backup repo on 2nd disk / NAS<br/><i>copy 2, different media</i>"]
  A --> C["Off-site copy<br/><i>copy 3 — cloud or remote</i>"]
  B --> D["3 copies · 2 media · 1 off-site"]
  C --> D

Three copies, two media types, one off-site — the 3-2-1 baseline.

restic, briefly

restic is a backup program that fits a homelab well: backups are encrypted (AES-256, mandatory), deduplicated at the block level so unchanged data is stored once, and a repository can live on a local disk, an SFTP target, or a cloud bucket — covering both your second-media and off-site copies with one tool (servercrate: restic Docker backup). borg is a close alternative with the same dedup-and-encrypt model; pick either. The commands below use restic.

Focus a backup on the data that would actually hurt to lose: your Docker volumes and bind mounts (database contents, uploaded files), and your compose.yaml and config files if they are not already in git. Anything trivially regenerated — pulled images, caches — you can skip.

Set it up

Initialize a repository once. Here it goes on a second disk; for off-site, the repository path becomes an SFTP or cloud URL instead. restic reads the repository location and password from environment variables so you do not retype them (restic docs: backing up). Set them in your shell first, on your Debian box:

export RESTIC_REPOSITORY=/mnt/backup/restic-repo
export RESTIC_PASSWORD='use-a-long-random-passphrase'

Then initialize the repository — this runs once:

restic init

It reports created restic repository ... at /mnt/backup/restic-repo.

Danger The repository password encrypts the backups, and there is no recovery if you lose it — no password, no restore, ever. Store it in a password manager before you create the repo, not on the same machine you are backing up.

Take a backup — run this in the same shell where you set the variables:

restic backup /srv/data /etc/your-app /home/you/compose

It scans the paths and finishes with a snapshot <id> saved line. Run that on a schedule (a systemd timer or cron job) and restic only uploads the changed blocks each time. List what you have:

restic snapshots

You get a table of snapshots with their IDs, timestamps, and paths.

Keep the history from growing forever with a retention policy. forget drops old snapshots and --prune reclaims the space they referenced (restic docs: removing snapshots):

restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

And verify the repository's internal structure is healthy — run this regularly, ideally on a schedule (restic docs: backing up):

restic check

A healthy repository ends with no errors were found.

Test the restore

Here is the part almost everyone skips, and it is the only part that matters.

⚠️ Warning A backup you have never restored is not a backup — it is a hope. Backup jobs fail silently all the time: a path typo, a full disk, a permission error, a corrupt repo. You only find out when you try to restore, and the worst time to discover your backups are empty is the moment you actually need them (oneuptime: back up and restore Docker volumes).

So restore on purpose, to a throwaway location, and check the data — run these on your Debian box:

restic restore latest --target /tmp/restore-test
ls -R /tmp/restore-test

restic restore reports restoring ... to /tmp/restore-test, and ls -R lists the recovered files so you can eyeball that they are really there.

For a database volume, go further: spin up a test container against the restored files and confirm the schema and row counts look right. The restore is what you are actually buying with all of this; prove it works. Do a trial restore at least once a quarter, and after any change to your backup setup (oneuptime: back up and restore Docker volumes).

flowchart LR
  A["Live data"] -->|"restic backup"| B["Repository<br/><i>encrypted, deduplicated</i>"]
  B -->|"restic snapshots"| C["Pick a snapshot"]
  C -->|"restic restore"| D["/tmp/restore-test"]
  D -->|"verify contents"| E["Confirmed restorable"]

Backup is only half the loop — the restore closes it.

A short close

Storage and backups are the unglamorous half of a homelab, and the half you will be most grateful for. Add a data disk the careful way — lsblk to find it, parted to partition, mkfs.ext4 to format, then a UUID line in /etc/fstab verified with findmnt --verify and mount -a before you ever reboot. Then back it up under the 3-2-1 rule with restic: three copies, two media, one off-site, with a retention policy and regular restic check. And test a restore — on a schedule, before you need it — because a backup you have never restored is just a guess. This is part 6 of Build a homelab on Debian; next up is monitoring your homelab, so you know something has broken before your backups have to save you.