TL;DR Drie kopieën, twee soorten media, één ervan buiten de deur. Restic voor bestanden, Velero voor het cluster, een bucket bij Backblaze als offsite kopie, en een restore-test op een vast moment. Bij mij kost dat zo’n 5 euro per maand, en die restore-test is het onderdeel waar ik mezelf nog steeds toe moet zetten.
Stel dat de disk in je homelab vannacht sterft. Niet corrupt raken, niet eerst een week SMART-waarschuwingen geven, gewoon weg. De git-server met elke repo waar je om geeft, de password vault, vijftien jaar familiefoto’s, de home automation die het huis aanstuurt. Allemaal op een drive die heeft besloten dat het mooi geweest is.
Kun je zeggen wat er daarna gebeurt zonder dat je maag samentrekt? Zo niet, dan heb je geen backups. Dan heb je hoop, en op hoop heb ik zelf langer gedraaid dan me lief is.
Het goede nieuws is dat je niks slims nodig hebt. De 3-2-1-regel bestaat al tientallen jaren en is bijna saai simpel. Deze post brengt je van “volgens mij heb ik wel iets” naar een setup die je getest hebt. Ik begin met de versie die je vandaag kunt draaien en stapel de rest daar bovenop.
De 3-2-1-regel
Drie kopieën, twee verschillende media, één offsite. Meer is de regel niet. De rest van deze post is die drie getallen invullen met echte tools.
Waarom drie kopieën
- Kopie 1: Je live data (origineel)
- Kopie 2: Lokale backup (snelle restore)
- Kopie 3: Offsite backup (disaster recovery)
Eén kopie is je data die zit te wachten tot hij kapotgaat. Twee kopieën voelen veiliger, precies tot één gebeurtenis ze allebei meeneemt. Brand doet dat. Een ransomware-payload die elke mount afloopt die hij kan bereiken ook. Drie kopieën met echte afstand ertussen, dáár zou ik het pas resilience durven noemen.
Waarom twee soorten media
Verschillende hardware gaat op verschillende manieren kapot, en je wilt je kopieën over die faalwijzen verdeeld hebben, zodat één slechte dag er niet bij allemaal kan.
- SSDs kunnen stilletjes falen via bit rot, waarbij data ongemerkt slecht wordt voordat je het doorhebt
- HDDs sterven mechanisch, vaak met genoeg waarschuwing om te reageren als je oplet
- RAID is geen backup. Het beschermt tegen een drive die wegvalt, en doet niets tegen corruptie, verwijdering, of een slechte write die netjes gespiegeld wordt over elke disk
Zet je kopieën op een SSD, een draaiende schijf en een bucket op afstand, en wat de ene sloopt, sloopt zelden de andere.
Waarom één offsite
Je huis kan afbranden. Een stroompiek kan alles koken wat aan hetzelfde circuit hangt. Lokale kopieën zijn snel en handig en waardeloos tegen alles wat het hele gebouw raakt. De offsite kopie is gewoon de kopie die in een ander gebouw staat.
Wat je back-upt
Voordat je iets back-upt, sorteer je je data in drie bakken. De meeste mensen back-uppen veel te veel, of ze slaan stilletjes het ene ding over dat ze niet kunnen herbouwen. Een paar minuten sorteren lost allebei op.
Kritiek, dagelijkse backup
| Data | Waarom | Tool |
|---|---|---|
| Databases | Kan niet herstellen | pg_dump, Velero |
| Secrets/credentials | Security kritiek | Vault export, External Secrets |
| Configuratie | Systeem staat | Git (al offsite) |
| Persoonlijke bestanden | Onvervangbaar | Restic |
Belangrijk, wekelijkse backup
| Data | Waarom | Tool |
|---|---|---|
| Container images | Rebuild kost tijd | Registry backup |
| Persistent volumes | Stateful workloads | Longhorn /Velero |
| Logs (gecomprimeerd) | Forensics | Loki snapshots |
Herbouwbaar, geen backup
- Basis OS (herinstalleer van ISO)
- Gedownloade packages (opnieuw downloaden)
- Gecachete data (regenereert)
- Tijdelijke bestanden
Kun je het terughalen met een commando of een download, laat het dan weg. Backupruimte kost geld en backuptijd kost tijd, en die aan een basis-OS besteden is zonde.
Backup-tools
Goed, dat was de theorie. Nu wordt het praktisch. Ik begin met de simpelste tool die je een echte tweede kopie geeft en bouw daarna op naar het Kubernetes-spul.
Restic voor file-backups
Doe je na het lezen maar één ding, zet dan Restic op. Dat gebruik ik zelf voor file-backups. Snel, versleutelt standaard, dedupliceert zodat herhaalde backups klein blijven, en praat met een lokale schijf of een S3-bucket zonder dat je iets aan je workflow verandert. Dit is de minimaal werkende backup, en hij draait binnen vijf minuten.
# Initialiseer repository
restic init --repo /mnt/backup/restic
# Of met S3 backend
restic init --repo s3:s3.amazonaws.com/my-bucket
# Backup een directory
restic backup /home/user/documents
# Backup met exclusies
restic backup /data \
--exclude="*.tmp" \
--exclude=".cache" \
--exclude="node_modules"
# Lijst snapshots
restic snapshots
# Restore
restic restore latest --target /restore/location
Restic automatisch
Een backup die je zelf moet onthouden is er een die je vergeet. Zet de commando’s in een script, wijs het naar je offsite bucket en laat het zelf prunen, zodat de repo niet eindeloos groeit.
#!/bin/bash
# /usr/local/bin/backup.sh
export RESTIC_REPOSITORY="s3:s3.eu-west-1.amazonaws.com/homelab-backups"
export RESTIC_PASSWORD_FILE="/etc/restic/password"
export AWS_ACCESS_KEY_ID="your-key"
export AWS_SECRET_ACCESS_KEY="your-secret"
# Backup
restic backup /data/important \
--exclude-caches \
--tag homelab \
--tag daily
# Prune oude snapshots (bewaar 7 dagelijks, 4 wekelijks, 12 maandelijks)
restic forget \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 12 \
--prune
# Check repository integriteit
restic check
Die restic check aan het eind doet er meer toe dan het lijkt. Hij leest de repository terug en controleert hem. Zonder die check heb je een map vol bytes en geen idee of ze nog leesbaar zijn.
Cronjob:
# /etc/cron.d/restic-backup
0 3 * * * root /usr/local/bin/backup.sh >> /var/log/restic-backup.log 2>&1
Velero voor Kubernetes
Zodra je een cluster draait, zijn file-backups niet meer genoeg. Je wilt ook de staat van het cluster zelf, de Deployments en ConfigMaps en PersistentVolumeClaims. Velero back-upt Kubernetes-resources en hun persistent volumes samen, dus één restore brengt de workloads én hun data terug.
# Installeer Velero met S3 backend
velero install \
--provider aws \
--plugins velero/velero-plugin-for-aws:v1.8.0 \
--bucket velero-backups \
--backup-location-config region=eu-west-1 \
--secret-file ./credentials-velero \
--use-volume-snapshots=true \
--snapshot-location-config region=eu-west-1
Geplande backups
apiVersion: velero.io/v1
kind: Schedule
metadata:
name: daily-backup
namespace: velero
spec:
schedule: "0 3 * * *"
template:
includedNamespaces:
- production
- gitlab
- monitoring
excludedResources:
- events
- pods
ttl: 720h # Bewaar 30 dagen
storageLocation: default
volumeSnapshotLocations:
- default
Restore vanuit Velero
# Lijst backups
velero backup get
# Beschrijf een backup
velero backup describe daily-backup-20260518030000
# Restore gehele backup
velero restore create --from-backup daily-backup-20260518030000
# Restore specifieke namespace
velero restore create --from-backup daily-backup-20260518030000 \
--include-namespaces gitlab
Longhorn-backups
Draai je Longhorn als storage, dan zit er al een backup-engine onder je volumes, en die praat rechtstreeks met S3:
# Configureer backup target
apiVersion: longhorn.io/v1beta1
kind: Setting
metadata:
name: backup-target
namespace: longhorn-system
value: "s3://longhorn-backups@eu-west-1/"
---
apiVersion: longhorn.io/v1beta1
kind: Setting
metadata:
name: backup-target-credential-secret
namespace: longhorn-system
value: "longhorn-s3-secret"
Terugkerende backups inplannen:
apiVersion: longhorn.io/v1beta1
kind: RecurringJob
metadata:
name: daily-backup
namespace: longhorn-system
spec:
cron: "0 3 * * *"
task: backup
groups:
- default
retain: 7
concurrency: 2
Offsite-opties
De “1” in 3-2-1 is het deel dat mensen overslaan, meestal omdat het klinkt als het dure, ingewikkelde deel. Valt mee. Je hebt drie eerlijke opties, ze ruilen kosten tegen controle, en hoe ver je dat lijstje afzakt zegt best wat over hoeveel sovereignty je wilt.
Cloudopslag
De goedkoopste manier om offsite te komen. Kies een provider, wijs Restic of Velero ernaartoe, klaar.
| Provider | Kosten | Voordelen | Nadelen |
|---|---|---|---|
| Backblaze B2 | $0.005/GB | Goedkoop, S3-compatible | US-gebaseerd |
| Wasabi | $0.0059/GB | Geen egress kosten | 90-dagen minimum |
| AWS S3 Glacier | $0.004/GB | Zeer goedkoop | Trage retrieval |
| Hetzner Storage Box | €3.81/1TB | EU-gebaseerd, goedkoop | Alleen SFTP/WebDAV |
Wil je de offsite kopie binnen de EU? Dan is de Hetzner Storage Box op prijs lastig te verslaan. Backblaze is de makkelijke default als je het gewoon werkend wilt hebben. Die van mij staat bij Backblaze, in de VS, dus over die EU-optie mag ik niet al te hard roepen.
Tweede locatie
Heb je een vriend of familielid die ook een homelab draait? Back-up elkaar. Zij bewaren een versleutelde kopie van jouw data, jij één van die van hen, en geen van beiden kan lezen wat er staat.
Dit kost je wat schijf en wat uploadbandbreedte, en je offsite kopie komt nooit bij een cloudprovider terecht.
Eigen cloud
De volle sovereignty-versie. Draai je eigen S3-compatible storage op een tweede fysieke locatie en back-up daarheen.
# MinIO op remote locatie
apiVersion: apps/v1
kind: Deployment
metadata:
name: minio
spec:
template:
spec:
containers:
- name: minio
image: minio/minio
args:
- server
- /data
env:
- name: MINIO_ROOT_USER
valueFrom:
secretKeyRef:
name: minio-credentials
key: user
- name: MINIO_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: minio-credentials
key: password
Bereik het via Tailscale, dan hoeft de storage nooit aan het publieke internet te hangen.
Database-backups
Databases vragen hun eigen aanpak. Kopieer je de bestanden onder een draaiende database vandaan, dan krijg je een snapshot die midden in een write kan zitten, en zo’n snapshot restoret misschien niet. Dump ze netjes.
PostgreSQL
#!/bin/bash
# Kubernetes PostgreSQL backup
NAMESPACE="gitlab"
POD=$(kubectl get pod -n $NAMESPACE -l app=postgresql -o jsonpath='{.items[0].metadata.name}')
DATE=$(date +%Y%m%d_%H%M%S)
# Dump alle databases
kubectl exec -n $NAMESPACE $POD -- \
pg_dumpall -U postgres | \
gzip > /backup/postgres_${DATE}.sql.gz
# Upload naar S3
restic backup /backup/postgres_${DATE}.sql.gz --tag postgres --tag daily
Vault-backup
Vault is degene waar ik wakker van lig, want Vault kwijt betekent de toegang tot al het andere kwijt. Maak een raft -snapshot, versleutel hem voordat hij de machine verlaat en stuur hem dan pas weg.
# Export Vault data (vereist root token)
vault operator raft snapshot save /backup/vault_$(date +%Y%m%d).snap
# Versleutel en upload
gpg --encrypt --recipient backup@example.com /backup/vault_$(date +%Y%m%d).snap
restic backup /backup/vault_$(date +%Y%m%d).snap.gpg --tag vault
Restores testen
Dit is het deel waar iedereen bij knikt en bijna niemand doet. Ik heb ook geknikt. Een backup die je niet getest hebt is een gok. De eerste restore moet niet de restore zijn die je echt nodig hebt, met productie plat, en dan blijkt dat de dumps al drie maanden leeg zijn.
Maandelijkse restore-test
Dus test op een vast moment. Restore naar een wegwerp-namespace, kijk of het ding opkomt, breek het weer af.
#!/bin/bash
# test-restore.sh
# Maak test namespace
kubectl create namespace restore-test
# Restore vanuit Velero
velero restore create test-restore \
--from-backup $(velero backup get -o json | jq -r '.items[0].metadata.name') \
--include-namespaces gitlab \
--namespace-mappings gitlab:restore-test
# Wacht op restore
velero restore wait test-restore
# Verifieer pods draaien
kubectl get pods -n restore-test
# Test applicatie (voorbeeld: GitLab)
kubectl port-forward -n restore-test svc/gitlab 8080:80 &
curl -s http://localhost:8080/health | grep "ok"
# Cleanup
kubectl delete namespace restore-test
Herstelprocedures opschrijven
Als er echt iets in brand staat, wil je je eigen restore-procedure niet uit je hoofd zitten reconstrueren. Schrijf per kritiek systeem het runbook op, met hoe lang het duurde en wanneer je voor het laatst hebt bewezen dat het werkt.
# GitLab Recovery Procedure
## Vereisten
- Toegang tot Velero backups
- Toegang tot PostgreSQL backups
- GitLab Helm values
## Stappen
1. Restore PostgreSQL vanuit backup
2. Restore GitLab PVCs met Velero
3. Deploy GitLab met zelfde Helm values
4. Verifieer user login werkt
5. Verifieer repositories bereikbaar zijn
## Geschatte Tijd: 45 minuten
## Laatst Getest: 2026-05-01
Die regel “Laatst Getest” houdt de test eerlijk. Staat er een datum van een jaar geleden, dan weet niemand of de procedure nog werkt, jij ook niet.
Backups monitoren
Een backup-job die twee weken geleden stilletjes gestopt is, is erger dan geen backup, want je denkt dat je gedekt bent. Hang de jobs aan je monitoring, zodat een failure tegen je schreeuwt.
Prometheus-alerts
groups:
- name: backup-alerts
rules:
- alert: BackupFailed
expr: restic_backup_last_successful_timestamp < (time() - 86400)
for: 1h
labels:
severity: critical
annotations:
summary: "Backup niet geslaagd in 24 uur"
- alert: BackupStorageLow
expr: restic_repository_size_bytes / restic_repository_max_bytes > 0.9
for: 1h
labels:
severity: warning
annotations:
summary: "Backup storage boven 90% vol"
Backup-dashboard
Bijhouden in Grafana:
- Laatste succesvolle backup tijd
- Backup duur trend
- Storage gebruik
- Restore test resultaten
Mijn backup-setup
Zo past het bij mij in elkaar, en dat is gewoon de lagen hierboven op elkaar gestapeld. Lokale kopieën voor snelle restores, een offsite bucket voor het rampscenario, en Git voor alles wat al tekst is.
Schema
| Wat | Frequentie | Retentie | Locatie |
|---|---|---|---|
| Longhorn snapshots | Per uur | 24 uur | Lokale NVMe |
| Longhorn backups | Dagelijks | 7 dagen | NAS |
| Velero volledige backup | Dagelijks | 30 dagen | Backblaze B2 |
| Database dumps | Dagelijks | 30 dagen | Backblaze B2 |
| Git repos | Push | Altijd | GitHub mirror |
Kosten
- Backblaze B2: ~€5/maand voor 200GB
- NAS storage: Al in bezit
- Totaal: ~€5/maand voor gemoedsrust
Vijf euro per maand, en de NAS stond er al. De foto’s zijn de reden dat ik het betaal.
Veelgemaakte fouten
De meeste hiervan heb ik zelf gemaakt, of bij vrienden zien gebeuren. Ze klinken allemaal redelijk, tot de dag dat ze je wat kosten.
“RAID is mijn backup”
RAID beschermt tegen een schijf die uitvalt. Het doet niks tegen:
- Per ongeluk verwijderen
- Ransomware
- Software bugs die data corrumperen
- Brand/overstroming/diefstal
Een rm -rf in de verkeerde directory staat meteen op elke schijf in de array. RAID houdt de array in de lucht als er een schijf sterft. Niks op dat lijstje is een schijf die sterft.
“Ik restore wel als het nodig is”
Heb je nog nooit gerestored, dan weet je niet of je backups werken, en de dag dat je ze nodig hebt is de dag dat je erachter komt. Minstens elk kwartaal testen.
“Ik back-up alles”
10TB aan films back-uppen die je opnieuw kunt downloaden, kost geld en tijd die je aan de data kunt besteden die je niet kunt vervangen. Eerst sorteren, dan het onvervangbare spul.
“Mijn backup staat in dezelfde kamer”
Eén brand neemt de server én de backup-schijf ernaast mee. Offsite is het deel dat de rest van de regel de moeite waard maakt.
Waarom dit ertoe doet
Dataverlies is geen misschien. Schijven hebben een jaarlijkse failure rate van 3-5%, mensen draaien het verkeerde commando, software maakt databases kapot op manieren die elke health check doorstaan, en huizen vatten af en toe vlam. Draai een homelab lang genoeg en je komt er minstens één van tegen.
Wat de uitkomst bepaalt is of je het saaie werk van tevoren gedaan hebt. Het is hetzelfde waar ik op deze blog steeds op uitkom, het verschil tussen een systeem dat je begrijpt en in de hand hebt en een systeem waarvan je hoopt dat het blijft staan. Met een geteste backup kost een kapotte schijf je een middag. Zonder kost hij je de foto’s.
Dus geef die vijf euro en die middag uit. En zet de restore-test in je agenda, want dat is de stap waar ik mezelf nog steeds toe moet zetten.
