Neu Durchstarten
Schritt für Schritt: Sichern von Daten eines Proxmox Hosts und Aufsetzen eines neuen Ubuntu Servers, der automatisierte Backups per ZFS und BTRFS macht.
Daten Sichern
Ein paar VMs und LXCs laufen noch am alten Proxmox Host pvebig. Ich sichere mit scp die Daten jener Applikationen, die ich weiter nutzen will, auf meinen neuen hpmirco Host.
Hierfür erstelle ich ein Backup Dataset, lege darin einen Order z.B. für Jellyfin an und berechtige meinen User darauf:
Dann kopiere ich von den VMs und LXCs all jene Daten, die ich brauche. Auch hier ein Beispiel von meinem Jellyfin Server:
Neuen Server einrichten
Ich nenne den neuen Server big. Es ist der Server, auf dem ich den den größten Datenpool einrichte. Zusätzlich sind dort 2 TB SSDs verbaut für guten Applikationsspeicher. Big wird der Server mit der Hauptlast meines Homelabs werden. Ich installiere wieder UbuntuServer mit SSH Server. Dann verbinde ich mich per SSH auf den Server und orientiere mich an DoItYourself
Pool big erstellen
Achtung, der Ansatz war nicht optimal, siehe Reboot und Ups.
Dieser Pool besteht aus 4 10TB HDDS und 2 kleinen SSDs. Letztere verwende ich als ZFS Special Devices. Bei den 10TB HDDs nehme ich wieder 3 mit Raid Z1 und einer Hot Spare.
Pool big für große Dateien tunen
Der Pool wird nur Daten hosten und generell eher größere Dateien im nutzen. Daher nutze ich einige Hinweise von kara systems:
sudo zfs set xattr=sa big
sudo zfs set atime=off big
sudo zfs set recordsize=1M big
sudo zfs set special_small_blocks=64K big
sudo zfs set compression=zstd big
Das ist der vorbereitete, noch leere Pool:

Pool fast erstellen
Dieser Pool besteht aus 2 2TB SSDs. Das wird ein einfacher Mirror und fürs erste mit erstmal keinem Tuning. Aber ich denke der oben verlinkte Artikel wird noch relevant, z.b. recordsize 8K für PosgreSQL Datenbanken.

Erste ZFS Datasets erstellen
- fast/app: Persistent Volumes für Docker Services und Vergleichbares.
- big/backup: Backups, inbesondere von anderen Hosts.
- big/data: Große, Logisch zusammengehörige Daten. Z.B. Medien, Fotos, Videos
Reboot und Ups
Es war Abend und ich hatte den Server ausgeschaltet. Am nächsten Tag wollte ich weiter an den ZFS Pools arbeiten und siehe da, ich habe ein Problem:
Mit jedem Reboot scheint sich die /dev/sd* Kennung der Disks zu verändern. Somit finden die Pools nicht alle ihre Disks und degraden. Also starte ich von vorne.
Den wipefs Command mache ich für alle Disks, die ich in den Pools hatte. So erreiche ich einen sauberen Startpunkt.
Pools mit Disk by-id erstellen
Die IDs ändern sich nicht, daher erstelle ich jetzt die Pools mit IDs.
Hierbei ist udiskctl status praktisch:

Und noch besser ls -l /dev/disk/by-id/ | grep ata

Mit den Infos erstelle ich den Pool big und Tune wie oben:
Der Pool fast ist dann auch schnell erstellt und als nächstes folgen die ZFS Datasets. Autocomplete ist beim Pool erstellen eine echte Hilfe!
Ich mache testweise einen Reboot und wie erwartet und gewünscht kommen die
Pools online:

Zwischenstatus
Ich habe jetzt 2 leere Pools und bin kurz davor, endlich Applikationen zum laufen zu bringen. Die letzte Voraussetzung ist, Snapshots und Backups einzurichten. Aus meiner Sicht ist dies wichtig, denn falls ich hier Probleme habe und nochmal von vorne Starten muss, dann ist es blöd, wenn ich schon Daten sichern muss. Aktuell ist noch alles sehr schnell wiederherstellbar.
Datensicherheit sicherstellen
Die Datensicherheit ist einer der Hauptgründe für mich, ZFS zu verwenden.
Die sanoid.conf Datei passe ich nur leicht an und ergänze die Datasets.
[fast/app]
production
zfs
[big/data]
production
zfs
Backup mache ich noch nicht, weil ich in hier in meiner initialen Einrichtung von hpmicro noch Fehlermeldungen im Snapshot monitoring bekommen habe.
Snapshots funktionieren schon einmal. Also geht's weiter mit Backups. Da ich den hpmicro als Backup Target verwenden will, habe lerne ich jetzt, das einzurichten.
Auf neuem Host big:
Auf dem Backup Target hpmicro:
ssh - Verbindung überprüfen und dabei den Host akzeptieren
Zusätzlich unmounte ich das Backup Dataset. Damit bekomme ich bei neuen Backups keine Mountprobleme. Und ich kann ja jederzeit das Dataset mounten, wenn ich etwas überprüfen möchte.
Dann ergänze ich die neuen Datasets im Syncoid Service (/etc/systemd/system/syncoid.service):
[Unit]
Description=Backup ZFS filesystems
Documentation=man:syncoid(8)
Requires=local-fs.target
After=network-online.target
Wants=network-online.target
#ConditionFileNotEmpty=/etc/sanoid/sanoid.conf
[Service]
Type=oneshot
Environment=TZ=UTC
User=recvuser
ExecStart=/usr/sbin/syncoid --recursive --no-privilege-elevation --no-sync-snap --sshkey /home/recvuser/.ssh/syncoid senduser@localhost:tank/app tank/backup/hpmicro/app
ExecStart=/usr/sbin/syncoid --recursive --no-privilege-elevation --no-sync-snap --sshkey /home/recvuser/.ssh/syncoid senduser@big:fast/app tank/backup/big/app
ExecStart=/usr/sbin/syncoid --recursive --no-privilege-elevation --no-sync-snap --sshkey /home/recvuser/.ssh/syncoid senduser@big:big/data tank/backup/big/data
Testen:
So sieht sein guter Output aus:

Snapshots und Backups prüfen
Das Mounten eines Snapshots ist leicht möglich und funktioniert recht intuitiv.
Damit ließen sich auch einzelne Files oder Directories von einem Snapshot oder Backup wiederherstellen. Natürlich nur, wenn's nötig ist.
Erste Applikationen
Nach all der Vorbereitung geht es jetzt los. Ich will den Host als Server für Applikationen nutzen, also muss ich die auch installieren.
Docker Management: komodo
Ein aus meiner Sicht sehr gutes Vorstellungsvideo hat jusec gemacht:
Um Docker managen zu können, muss ich zunächst Docker installieren:
Dann lege ich ein eigenes Verzeichnis für Komodo an, lade das Compose und .env File, editiere es für meine Zwecke und starte den docker compose stack.
&&
Auf http://big:9120/ kann ich das UI öffnen. Es wirkt auf mich recht voll mit Features. Ich muss mich mal ein wenig damit beschäftigen.
hpmicro zu komodo hinzufügen
Das schöne an Komodo ist, dass ich offensichtlich von einer Stelle aus alle verbundenden Server im Blick haben kann und entsprechend die Services managen kann. Also verbinde ich auch den hpmicro.
In der Dokumentation ist beschrieben, dass man das per systemd Service oder als Docker Container machen kann. Ich nehme Docker, da ich davon ausgehe, dass ich hier leichter Upgraden kann. Das beschriebene Docker Compose passe ich leicht an, ich ergänze den Wert für PERIPHERY_PASSKEYS. Und ich öffne den Port 8120 im compose.yaml.
Da hpmicro als Offsite Backup dienen wird, richte ich die Verbindung per netbird ein. Also noch schnell mit dem im netbird ui angezeigten Befehl netbird auf big installieren.
Beim Hinzufügen in komodo Web UI musste ich 2 Dinge tun:
- Als Adresse die Netbird Adresse angeben: https://hpmicro.netbird.cloud:8120
- Den Server auf Enabled stellen.
Ersten Stack in komodo einrichten: stream
Der erste Stack ist mein Medien Streaming Setup. Dreh- und Angelpunkt ist hier Jellyfin. Ich probiere es per Web UI.
Zuvor muss ich noch die entsprechenden Directories und Datasets am Host Big einrichten:
Quicksync ist für jellyfin angeblich wichtig. Da ich unter /dev/dri/render nichts finde, aber einen Eintrag mit /dev/dri/card0, der der group Video zugeordnet ist, hoffe ich dass das Quicksync ist. Mal sehen. So sieht das intitiale compose.yaml aus:
services:
jellyfin:
image: jellyfin/jellyfin
container_name: jellyfin
group_add:
- "44" # This needs to be the group id of your GPU, e.g., `stat -c '%g' /dev/dri/renderD128` on the docker host for iGPU
environment:
- TZ=Europe/Vienna
volumes:
- /fast/app/jellyfin/config:/config
- /fast/app/jellyfin/cache:/cache
- /big/data/youtube-kids:/youtube-kids
ports: # You will need to uncomment if you aren't running through a proxy
- 8096:8096
- 8920:8920 #optional
- 7359:7359/udp #optional
- 1900:1900/udp #optional
devices: # uncomment these and amend if you require GPU accelerated transcoding
- /dev/dri/:/dev/dri/
restart: unless-stopped
Ich mounte einen Snapshot des Backups der youtube-kids Daten. Ich kopiere sie per scp in das neu erstellte Dataset youtube-kids.
Jellyfin richte ich mit keinen besonderen Einstellungen ein. Dabei aktiviere ich Quicksync noch nicht, um zu sehen, ob ich es benötige. Während das Kopieren noch läuft streame ich schon auf 2 Geräten. Bisher sehr passt die Performance.
Stream um den Youtube Download Service pinchflat erweitern
Hierfür braucht es wiederum eigene Directories. Auch hier hatte ich die Daten für die Kinder gesichert.
In komodo ergänze ich den Service im compose.yaml und redeploye. Das ist wirklich einfach.
services:
jellyfin:
image: jellyfin/jellyfin
container_name: jellyfin
group_add:
- "44" # This needs to be the group id of your GPU, e.g., `stat -c '%g' /dev/dri/renderD128` on the docker host for iGPU
environment:
- TZ=Europe/Vienna
volumes:
- /fast/app/jellyfin/config:/config
- /fast/app/jellyfin/cache:/cache
- /big/data/youtube-kids:/youtube-kids
ports: # You will need to uncomment if you aren't running through a proxy
- 8096:8096
- 8920:8920 #optional
- 7359:7359/udp #optional
- 1900:1900/udp #optional
devices: # uncomment these and amend if you require GPU accelerated transcoding
- /dev/dri/:/dev/dri/
restart: unless-stopped
pinchflat-kids:
image: ghcr.io/kieraneglin/pinchflat:latest
container_name: pinchflat-kids
restart: unless-stopped
environment:
# Set the timezone to your local timezone
- TZ=Europe/Vienna
ports:
- "8946:8945"
volumes:
- /fast/app/pinchflat/kids:/config
- /big/data/youtube-kids:/downloads
Unter http://big:8946 ist das pinchflat UI errreichbar und es hat schon aktive Tasks. Im Verzeichnis /big/data/youtube-kids sind die neuen Downloads auch sichtbar. In den jellyfin Einstellungen habe ich den Bibliothek - Scan auf stündlich gestellt, also müsste ich noch ein wenig warten, bis ich die neuen Videos auch in jellyfin sehe. Natürlich kann man auch manuell scannen und da ich ungeduldig bin, klicke ich schon auf den Button. Tara, die neuen Videos sind in jellyfin abrufbar!
Zugriff von außen auf die Ressourcen des Homelabs
Ich möchte mein Netzwerk zuhause nur bedingt für direkten Zugriff von außen öffnen. Der Plan ist, einem eigenen MiniPC den Zugriff auf andere Geräte des Netzwerks zu verbieten und ihn nur Zugriff auf das Internet zu geben. Zu diesen MiniPC hin öffne ich die notwendigen Ports. Und von dort gehts dann per VPN Tunnel zu meinen Services.
Der ich denke recht neue Service bietet mir einen Tunnel von einem VPS direkt auf die Server des Homelabs. Zusätzlich kommt da ein Reverse-Proxy und sogar Crowdsec mit dazu. Es gibt aktuelle Tutorials und Youtube Content. Das alles bringt mich dazu, mich mit Pangolin zu beschäftigen. Nur will ich keinen VPS nutzen, sondern auch einen Server zuhause.
Den neuen Server konfigurieren und erste Sicherheitsvorkehrungen treffen
Mein MiniPC ist ein gigabyte brix. Daher nenne ich ihn gia. Er ist aus dem Internet erreichbar und damit muss ich zunächst ein paar Basiseinrichtungen vornehmen:
Jetzt kann ich mich über meinen eigenen User verbinden und den root für ssh Login sperren. /etc/ssh/sshd_config:
2m
no
yes
6
10
Dann mit sudo systemctl restart ssh den ssh service neu Starten und in einem
neuen Shell testen, ob ich mich mit meinem User noch verbinden kann.
Das klappt und jetzt versuche ich noch einen Login mit root. Das ist nicht mehr
möglich, also passt das auch.
Später werde ich SSH noch viel besser konfigurieren. Es ist schon spät, SSH ist noch nicht mit Key Authentication abgesichert. Firewall ist auch noch keine konfiguriert. D.h. den MiniPC fahre ich herunter und kümmere mich morgen darum. Ich möchte ihn nicht stundenlang so offen im Internet laufen haben.
Der nächste Abend... Giga ist wieder gestartet und zwischenzeitlich habe ich in meinem Passwort Manager Bitwarden einen ssh Key erstellt. Den Public Teil des Keys ergänze ich als authorisierten Schlüssel auf giga:
In einer neuen Terminal Session teste ich erfolgreich, ob der SSH Zugang per Key funktioniert. Dann sperre ich Passwort basierten SSH Zugang im /etc/ssh/sshd_config:
no
Wieder ssh restarten und testen. Passwort geht nicht mehr. Key geht.
SSH ist im Mindeststandard aus meiner Sicht abgesichert. Jetzt kommt die Firewall. Auch hier will ich mit einem sinnvollen Mindeststandard starten. UFW ist installiert und läuft schon. Also nutze ich das.
UFW funktioniert und der Server ist grundsätzlich mal für SSH und Zugriff ins Internet offen. Aber keine der anderen möglichen IP Adressen im LAN ist für den Server jetzt erreichbar.
Mehr braucht es denke ich mal nicht.
Pangolin installieren
Die Dokumentation listet sehr gut auf, welche Voraussetzungen zu erfüllen sind. Mir fehlt noch ein offener Port in der Firewall:
Die richtigen DNS Einträge hatte ich zuvor schon erstellt. In der Fritzbox mache ich die Portweiterleitungen per "Freigaben":

Bei der Installation vertraue ich auf das Skript von fossorial.
# wget command von Dokumentation
Ich beantworte alle Fragen und so komme ich zu einer laufenden Pangolin Instanz mit Crowdsec. Das hat nur einige Minuten gedauert und der erste Login ist schon mit HTTPS und gültigem Zertifikat. Gleich beim ersten Login werde ich durch das Initiale Setup geführt. Auch das ist intuitiv und so habe ich jetzt einen Tunnel von Pangolin zum Server big. Wow! Das Setup läuft gut!
Beim nächsten mal werde ich noch das Initiale Passwort gleich mit einem Passwort Manager generieren. Denn es lässt sich nicht einfach im Web-UI ändern. Ich habe entdeckt, dass sich das über docker-compose anpassen lässt. Werde ich machen.
Meine Services mit HTTPS erreichbar machen
Ich starte mit Jellyfin, meinem Medien Streaming Service. Lt. Dokumentation muss ich eine neue "Resource" erzeugen. Das geht im Web-UI und ist logisch aufgebaut. Unter Proxy gebe ich als Hostname localhost an und als Port den für Jellyfin konfigurierten Port 8096. Unter Authentication deaktiviere ich vorerst "Use Platform SSO". Das ist zwar mein Ziel, aber jetzt will ich erstmal wissen, ob die Proxy funktioniert.
Zunächst habe ich unter meiner gewünschten Subdomain ein "Bad Gateway" als Response erhalten. Der Fehler war im Host "localhost". Ich musste die IP angeben. Dann hats geklappt. Der Hostname big ging auch nicht.
In meinem "Stream" Stack ist auch pinchflat. Das hat keinen eigenen Login. Damit ist es der perfekte erste Kandidat für SSO von Pangolin. Ich denke, dass dies einen Login bei Pangolin erfordert, bevor ich zu pinchflat weitergeleitet werde. Und so ist es! In der gleichen Browser Session wo ich in Pangolin eingelogt bin, komme ich direkt auf die Web-UI von pinchflat. Aber wenn ich ein privates Fenster (=Neue Browser Session) öffne und die Url ansurfe, kommt "Authentication Required". Erst nach dem Login gehts auf die gewünschte Seite. Das ist echt super! Und sehr einfach einzurichten. Da es so einfach ist, aktiviere ich mit einem Klick den SSO für Jellyfin. Und ein paar Minuten später deaktiviere ich SSO wieder, weil die Jellyfin App am Iphone damit nicht in den Login kommt. Das ist ein Thema für mein zukünftiges Ich.
Datenbackups erstellen ohne ZFS, dafür mit btrfs und btrbk
Mein Host "giga" ist einer jener MiniPCs, bei denen keine zweite Festplatte eingebaut werden kann. Daher habe ich bei Installation das root Filesystem mit btrfs formatiert. btrfs ist auch ein Copy-on-write Filesystem, damit sind analog zu ZFS snapshots möglich. Mein Plan ist analog zu meinem ZFS Setup mit sanoid und syncoid auch auf giga lokale Snapshots jener Directories zu machen, die relevante Applikationsdaten enthalten. Zusätzlich will ich auch hier eine remote Datensicherung auf hpmicro durchführen. Los geht's.
Zunächst mache ich aus /opt ein btrfs subvolume. So sind Snapshots ermöglicht.
Die Container starten brav und da im docker-compose.yml die Pfade relativ zum File definiert sind, finden die Container ihre Daten.
Jetzt muss ich noch sicherstellen, dass dieses subvolume immer gemounted wird:
Zeigt mir die ID des Subolumes an. Das ergänze ich im fstab File:
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>
# / was on /dev/ubuntu-vg/ubuntu-lv during curtin installation
/dev/disk/by-id/dm-uuid-LVM-W69HzvaX7IdbljrLPr5Jo9WVTRevIQtBcDLQmXeSwsiSC3jabnM9SB0CYN9PBFcx / btrfs defaults 0 1
# /boot was on /dev/sda2 during curtin installation
/dev/disk/by-uuid/c718811d-da1d-488c-ba1b-0d13b36714d2 /boot ext4 defaults 0 1
/swap.img none swap sw 0 0
# Manuell ergänzt
/dev/disk/by-id/dm-uuid-LVM-W69HzvaX7IdbljrLPr5Jo9WVTRevIQtBcDLQmXeSwsiSC3jabnM9SB0CYN9PBFcx /opt/docker btrfs subvolid=256,defaults,noatime,space_cache,autodefrag,compress=zstd 0 0
Dann fahre ich sicherheitshalber nochmal die Container runter und teste den fstab mount:
Dann starte ich den Server sicherheitshalber mal neu und da ich die Daten im Pfad /opt/docker/pangolin finde, starte ich mit docker compose up -d die Container wieder. Damit ist der Umzug auf ein btfrs subvolume abgeschlossen und erfolgreich getestet.
Snapshots und Backups per btrbk
Jetzt brauche ich nur noch Snapshots und Backups. Dafür nutze ich btrbk. Auch das lässt sich leicht per apt installieren. Im folgenden ist mein Installations - und Konfigurationsprozess aus einer bereinigten Bash History.
Die btrbk.conf habe ich um alle Beispiele bereinigt und folgende Konfigurationen definiert:
snapshot_dir /btrbk_snapshots
# Meine Konfiguration
subvolume /opt/docker
Damit kann ich manuell snapshots erstellen. Auch wenn das btrbk.conf im ersten Moment etwas überladen wirkt, es dokumentiert gut und schlussendlich sind es wenige Einstellungen, die vorzunehmen sind.
Um auf meine ZFS Backups die Daten sichern zu können, muss ich mich zunächst um Zugriffe kümmern. Da ich den Host giga komplett im Netzwerk isoliert habe, greife ich auf netbird zurück und installiere es mit dem im netbird ui angegebenen Befehlen. Wichtig: Session Expiration disablen!
Dann muss ich den hpmicro für den Empfang von Backups vorbereiten. Hierfür gibt's einen eigenen User und natürlich ein eigenes zfs dataset. Dieses muss ich mounten, sonst findet btrbk das Target nicht. (Das ist kein Thema bei syncoid.) Das ssh keypair habe ich wieder im Passwort Vault generiert. Diese Befehle führe ich alle auf hpmicro aus.
Auf giga muss ich den Private Key abspeichern und die btrbk.conf erweitern:
# Specify SSH private key for remote connections
ssh_identity /etc/btrbk/ssh/btrbk
ssh_user recvbtrbk
# Meine Konfiguration
subvolume /opt/docker
target raw ssh://hpmicro/tank/backup/giga
Mit Ausführung von btrbk wird ein lokaler Snapshot erstellt und einer zu hpmicro gesendet.
Automatisieren
Jetzt richte ich in /etc/systemd/system noch den btrbk.service und den btrbk.timer ein:
[Unit]
Description=Backup BTRFS filesystems
Documentation=man:btrbk
Requires=local-fs.target
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
Environment=TZ=UTC
User=root
ExecStart=/usr/bin/btrbk run
[Unit]
Description=Run btrbk hourly
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.target
Ich warte die nächste volle Stunde ab und prüfe, ob ein neuer Snapshot remote auf hpmicro ankommt. Das tut er nicht, aber ein lokaler Snapshot ist sichtbar. Vermutung: nur einmal täglich ein remoter Snapshot? Muss ich noch prüfen.
Feinkonfiguration von btrbk
Ich möchte die Snapshots und Backups analog zu syncoid und sanoid verfügbar haben. Hier vertraue ich darauf, dass die Empfehlungen der Profis gut passen. Damit muss ich herausfinden, wie das bei sanoid konfiguriert ist. Dafür reicht ein Blick in die Konfigurationsdatei /etc/sanoid/sanoid.conf
[template_production]
36
30
3
0
[template_backup]
30
90
12
0
Das baue ich in /etc/btrbk/btrbk.conf nach:
2d
36h 30d 4w 3m 0y
10d
30h 90d 10w 12m 0y
Ich werde in der nächsten Zeit immer mal wieder überprüfen, ob ich die entsprechenden Snapshots und Backups finde. Oben hatte ich mich gefragt, warum täglich nur ein remoter Snapshot zu sehen war auf hpmicro. Jetzt, nachdem ich die Einstellungen geändert habe, sehe ich alle remoten Snapshots/Backups. Also lag es nur an der Konfiguration. Die Frage konnte ich mir schnell selbst beantworten. Hurra!
Fazit
Dieser Artikel ist schon recht lange und ich denke, jetzt ist ein guter Cut. Ich habe mehrere Server inkl. Backups auf andere Server. Erste Services laufen und ich denke, ich habe eine gute Ausgangsbasis für die nächsten Schritte. Das werden denke ich wieder etwas kürzere, spezifischere Artikel. Es gibt noch einiges zu tun... Jellyfin ist noch nicht für mich eingerichtet und auch in der Infrastrutur mit Komodo und Pangolin habe ich erst an der Oberfläche gekratzt. Ich freue mich drauf, hier mehr ins Detail zu gehen.
Matthias