Neu Durchstarten

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:

sudo zfs create tank/backup/pvebig
sudo mkdir tank/backup/pvebig/jellyfin
sudo chown matthias:matthias tank/backup/pvebig/jellyfin

Dann kopiere ich von den VMs und LXCs all jene Daten, die ich brauche. Auch hier ein Beispiel von meinem Jellyfin Server:

scp -r youtube-kids matthias@<ip von hpmicro>:/tank/backup/pvebig/jellyfin/

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

ssh matthias@big
sudo apt upgrade -y
sudo apt install zfsutils-linux

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.

sudo zpool create -f big raidz1 /dev/sda /dev/sdb /dev/sdc
sudo zpool add big spare /dev/sdd
sudo zpool set autoreplace=on big
zpool get autoreplace big
sudo zpool add big special mirror /dev/sdf /dev/sdh

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 big

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.

sudo zpool create -f fast mirror /dev/sdi /dev/sdk

Pool fast

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
sudo zfs create fast/app
sudo zfs create big/backup
sudo zfs create big/data

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.

sudo zpool destroy big
sudo zpool destroy fast
sudo wipefs --all /dev/sda

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: Übersicht über Disks

Und noch besser ls -l /dev/disk/by-id/ | grep ata Übersicht über Disks by id

Mit den Infos erstelle ich den Pool big und Tune wie oben:

zpool create big raidz1 /dev/disk/by-id/ata-HGST_HUH721010ALE600_7PK77KBC /dev/disk/by-id/ata-HGST_HUH721010ALE600_7P016KVG /dev/disk/by-id/ata-ST10000VN0004-1ZD101_ZA21DLRS
zpool add big spare /dev/disk/by-id/ata-ST10000VN0004-1ZD101_ZA20K43N
zpool set autoreplace=on big
zpool add big special mirror /dev/disk/by-id/ata-SanDisk_SSD_PLUS_240GB_234505A0074B /dev/disk/by-id/ata-SanDisk_SSD_PLUS_240GB_23456M453806
zfs set xattr=sa big
zfs set atime=off big
zfs set recordsize=1M big
zfs set special_small_blocks=64K big
zfs set compression=zstd big

Der Pool fast ist dann auch schnell erstellt und als nächstes folgen die ZFS Datasets. Autocomplete ist beim Pool erstellen eine echte Hilfe!

sudo zpool create fast mirror /dev/disk/by-id/ata-SanDisk_SSD_PLUS_2000GB_21011T801341 /dev/disk/by-id/ata-SanDisk_SSD_PLUS_2000GB_23370Y800243
sudo zfs create fast/app
sudo zfs create big/backup
sudo zfs create big/data

Ich mache testweise einen Reboot und wie erwartet und gewünscht kommen die Pools online: Die beiden Pools

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.

sudo apt install sanoid
sudo mkdir /etc/sanoid/
sudo cp /usr/share/doc/sanoid/examples/sanoid.conf /etc/sanoid/sanoid.conf

Die sanoid.conf Datei passe ich nur leicht an und ergänze die Datasets.

[fast/app]
        use_template = production
        recursive = zfs

[big/data]
        use_template = production
        recursive = 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:

sudo useradd -m senduser
sudo useradd -m recvuser
sudo su senduser
mkdir ~/.ssh
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICyRsfv1oYaDee7tPvEAqz1NzKQOFxnzjknnOIXC5soQ syncoid" >> ~/.ssh/authorized_keys

Auf dem Backup Target hpmicro:

ssh - Verbindung überprüfen und dabei den Host akzeptieren

sudo su recvuser
ssh -i ~/.ssh/syncoid senduser@big

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.

sudo zfs set mountpoint=none tank/backup

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:

sudo systemctl daemon-reload
sudo systemctl start syncoid.service
systemctl status syncoid.service

So sieht sein guter Output aus: syncoid.service erfolgreich

Snapshots und Backups prüfen

Das Mounten eines Snapshots ist leicht möglich und funktioniert recht intuitiv.

sudo mount -t zfs <voller Path des Snapshots> <mountmount z.B. /mnt/temp>

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:

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh ./get-docker.sh
sudo usermod -aG docker $USER

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.

wget -P komodo https://raw.githubusercontent.com/moghtech/komodo/main/compose/mongo.compose.yaml &&   wget -P komodo https://raw.githubusercontent.com/moghtech/komodo/main/compose/compose.env
mv komodo/* . 
rm -r komodo
mv mongo.compose.yaml compose.yaml
vim compose.env 
vim compose.yaml 
mkdir mongo-data
mkdir mongo-config
mkdir repo-cache
docker compose up -d

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:

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:

mkdir -p /fast/app/jellyfin/config /fast/app/jellyfin/cache
sudo zfs create big/data/youtube-kids

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.

cd /big/data
sudo scp -r matthias@192.168.178.128:/mnt/tmp/jellyfin/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.

mkdir /fast/app/pinchflat
cd /fast/app/
sudo scp -r matthias@hpmicro:/mnt/tmp/pinchflat .

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:

sudo apt update
sudo apt upgrade
reboot

Jetzt kann ich mich über meinen eigenen User verbinden und den root für ssh Login sperren. /etc/ssh/sshd_config:

# Authentication:

LoginGraceTime 2m
PermitRootLogin no
StrictModes yes
MaxAuthTries 6
MaxSessions 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:

cd ~/.ssh
vim authorized_keys # Das File ist noch leer, daher einfach die erste Zeile

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:

PasswordAuthentication no

Wieder ssh restarten und testen. Passwort geht nicht mehr. Key geht.

ssh -i ~/.ssh/public <username>@giga

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.

ss -nptl # Offene Ports checken
sudo ufw allow OpenSSH
sudo ufw deny out from any to 192.168.178.2
sudo ufw deny out from any to 192.168.178.3
sudo ufw deny out from any to 192.168.178.4
sudo ufw deny out from any to 192.168.178.5
sudo ufw deny out from any to 192.168.178.6
sudo ufw deny out from any to 192.168.178.7
sudo ufw deny out from any to 192.168.178.8
sudo ufw deny out from any to 192.168.178.9
sudo ufw deny out from any to 192.168.178.10
sudo ufw deny out from any to 192.168.178.11
sudo ufw deny out from any to 192.168.178.12
sudo ufw deny out from any to 192.168.178.13
sudo ufw deny out from any to 192.168.178.14
sudo ufw deny out from any to 192.168.178.15
sudo ufw deny out from any to 192.168.178.16
sudo ufw deny out from any to 192.168.178.17
sudo ufw deny out from any to 192.168.178.18
sudo ufw deny out from any to 192.168.178.19
sudo ufw deny out from any to 192.168.178.20
sudo ufw deny out from any to 192.168.178.21
sudo ufw deny out from any to 192.168.178.22
sudo ufw deny out from any to 192.168.178.23
sudo ufw deny out from any to 192.168.178.24
sudo ufw deny out from any to 192.168.178.25
sudo ufw deny out from any to 192.168.178.26
sudo ufw deny out from any to 192.168.178.27
sudo ufw deny out from any to 192.168.178.28
sudo ufw deny out from any to 192.168.178.29
sudo ufw deny out from any to 192.168.178.30
sudo ufw deny out from any to 192.168.178.31
sudo ufw deny out from any to 192.168.178.32
sudo ufw deny out from any to 192.168.178.33
sudo ufw deny out from any to 192.168.178.34
sudo ufw deny out from any to 192.168.178.35
sudo ufw deny out from any to 192.168.178.36
sudo ufw deny out from any to 192.168.178.37
sudo ufw deny out from any to 192.168.178.38
sudo ufw deny out from any to 192.168.178.39
sudo ufw deny out from any to 192.168.178.40
sudo ufw deny out from any to 192.168.178.41
sudo ufw deny out from any to 192.168.178.42
sudo ufw deny out from any to 192.168.178.43
sudo ufw deny out from any to 192.168.178.44
sudo ufw deny out from any to 192.168.178.45
sudo ufw deny out from any to 192.168.178.46
sudo ufw deny out from any to 192.168.178.47
sudo ufw deny out from any to 192.168.178.48
sudo ufw deny out from any to 192.168.178.49
sudo ufw deny out from any to 192.168.178.50
sudo ufw deny out from any to 192.168.178.51
sudo ufw deny out from any to 192.168.178.52
sudo ufw deny out from any to 192.168.178.53
sudo ufw deny out from any to 192.168.178.54
sudo ufw deny out from any to 192.168.178.55
sudo ufw deny out from any to 192.168.178.56
sudo ufw deny out from any to 192.168.178.57
sudo ufw deny out from any to 192.168.178.58
sudo ufw deny out from any to 192.168.178.59
sudo ufw deny out from any to 192.168.178.60
sudo ufw deny out from any to 192.168.178.61
sudo ufw deny out from any to 192.168.178.62
sudo ufw deny out from any to 192.168.178.63
sudo ufw deny out from any to 192.168.178.64
sudo ufw deny out from any to 192.168.178.65
sudo ufw deny out from any to 192.168.178.66
sudo ufw deny out from any to 192.168.178.67
sudo ufw deny out from any to 192.168.178.68
sudo ufw deny out from any to 192.168.178.69
sudo ufw deny out from any to 192.168.178.70
sudo ufw deny out from any to 192.168.178.71
sudo ufw deny out from any to 192.168.178.72
sudo ufw deny out from any to 192.168.178.73
sudo ufw deny out from any to 192.168.178.74
sudo ufw deny out from any to 192.168.178.75
sudo ufw deny out from any to 192.168.178.76
sudo ufw deny out from any to 192.168.178.77
sudo ufw deny out from any to 192.168.178.78
sudo ufw deny out from any to 192.168.178.79
sudo ufw deny out from any to 192.168.178.80
sudo ufw deny out from any to 192.168.178.81
sudo ufw deny out from any to 192.168.178.82
sudo ufw deny out from any to 192.168.178.83
sudo ufw deny out from any to 192.168.178.84
sudo ufw deny out from any to 192.168.178.85
sudo ufw deny out from any to 192.168.178.86
sudo ufw deny out from any to 192.168.178.87
sudo ufw deny out from any to 192.168.178.88
sudo ufw deny out from any to 192.168.178.89
sudo ufw deny out from any to 192.168.178.90
sudo ufw deny out from any to 192.168.178.91
sudo ufw deny out from any to 192.168.178.92
sudo ufw deny out from any to 192.168.178.93
sudo ufw deny out from any to 192.168.178.94
sudo ufw deny out from any to 192.168.178.95
sudo ufw deny out from any to 192.168.178.96
sudo ufw deny out from any to 192.168.178.97
sudo ufw deny out from any to 192.168.178.98
sudo ufw deny out from any to 192.168.178.99
sudo ufw deny out from any to 192.168.178.100
sudo ufw deny out from any to 192.168.178.101
sudo ufw deny out from any to 192.168.178.102
sudo ufw deny out from any to 192.168.178.103
sudo ufw deny out from any to 192.168.178.104
sudo ufw deny out from any to 192.168.178.105
sudo ufw deny out from any to 192.168.178.106
sudo ufw deny out from any to 192.168.178.107
sudo ufw deny out from any to 192.168.178.108
sudo ufw deny out from any to 192.168.178.109
sudo ufw deny out from any to 192.168.178.110
sudo ufw deny out from any to 192.168.178.111
sudo ufw deny out from any to 192.168.178.112
sudo ufw deny out from any to 192.168.178.113
sudo ufw deny out from any to 192.168.178.114
sudo ufw deny out from any to 192.168.178.115
sudo ufw deny out from any to 192.168.178.116
sudo ufw deny out from any to 192.168.178.117
sudo ufw deny out from any to 192.168.178.118
sudo ufw deny out from any to 192.168.178.119
sudo ufw deny out from any to 192.168.178.120
sudo ufw deny out from any to 192.168.178.121
sudo ufw deny out from any to 192.168.178.122
sudo ufw deny out from any to 192.168.178.123
sudo ufw deny out from any to 192.168.178.124
sudo ufw deny out from any to 192.168.178.125
sudo ufw deny out from any to 192.168.178.126
sudo ufw deny out from any to 192.168.178.127
sudo ufw deny out from any to 192.168.178.128
sudo ufw deny out from any to 192.168.178.129
sudo ufw deny out from any to 192.168.178.130
sudo ufw deny out from any to 192.168.178.131
sudo ufw deny out from any to 192.168.178.132
sudo ufw deny out from any to 192.168.178.133
sudo ufw deny out from any to 192.168.178.134
sudo ufw deny out from any to 192.168.178.135
sudo ufw deny out from any to 192.168.178.136
sudo ufw deny out from any to 192.168.178.137
sudo ufw deny out from any to 192.168.178.138
sudo ufw deny out from any to 192.168.178.139
sudo ufw deny out from any to 192.168.178.140
sudo ufw deny out from any to 192.168.178.141
sudo ufw deny out from any to 192.168.178.142
sudo ufw deny out from any to 192.168.178.143
sudo ufw deny out from any to 192.168.178.144
sudo ufw deny out from any to 192.168.178.145
sudo ufw deny out from any to 192.168.178.146
sudo ufw deny out from any to 192.168.178.147
sudo ufw deny out from any to 192.168.178.148
sudo ufw deny out from any to 192.168.178.149
sudo ufw deny out from any to 192.168.178.150
sudo ufw deny out from any to 192.168.178.151
sudo ufw deny out from any to 192.168.178.152
sudo ufw deny out from any to 192.168.178.153
sudo ufw deny out from any to 192.168.178.154
sudo ufw deny out from any to 192.168.178.155
sudo ufw deny out from any to 192.168.178.156
sudo ufw deny out from any to 192.168.178.157
sudo ufw deny out from any to 192.168.178.158
sudo ufw deny out from any to 192.168.178.159
sudo ufw deny out from any to 192.168.178.160
sudo ufw deny out from any to 192.168.178.161
sudo ufw deny out from any to 192.168.178.162
sudo ufw deny out from any to 192.168.178.163
sudo ufw deny out from any to 192.168.178.164
sudo ufw deny out from any to 192.168.178.165
sudo ufw deny out from any to 192.168.178.166
sudo ufw deny out from any to 192.168.178.167
sudo ufw deny out from any to 192.168.178.168
sudo ufw deny out from any to 192.168.178.169
sudo ufw deny out from any to 192.168.178.170
sudo ufw deny out from any to 192.168.178.171
sudo ufw deny out from any to 192.168.178.172
sudo ufw deny out from any to 192.168.178.173
sudo ufw deny out from any to 192.168.178.174
sudo ufw deny out from any to 192.168.178.175
sudo ufw deny out from any to 192.168.178.176
sudo ufw deny out from any to 192.168.178.177
sudo ufw deny out from any to 192.168.178.178
sudo ufw deny out from any to 192.168.178.179
sudo ufw deny out from any to 192.168.178.180
sudo ufw deny out from any to 192.168.178.181
sudo ufw deny out from any to 192.168.178.182
sudo ufw deny out from any to 192.168.178.183
sudo ufw deny out from any to 192.168.178.184
sudo ufw deny out from any to 192.168.178.185
sudo ufw deny out from any to 192.168.178.186
sudo ufw deny out from any to 192.168.178.187
sudo ufw deny out from any to 192.168.178.188
sudo ufw deny out from any to 192.168.178.189
sudo ufw deny out from any to 192.168.178.190
sudo ufw deny out from any to 192.168.178.191
sudo ufw deny out from any to 192.168.178.192
sudo ufw deny out from any to 192.168.178.193
sudo ufw deny out from any to 192.168.178.194
sudo ufw deny out from any to 192.168.178.195
sudo ufw deny out from any to 192.168.178.196
sudo ufw deny out from any to 192.168.178.197
sudo ufw deny out from any to 192.168.178.198
sudo ufw deny out from any to 192.168.178.199
sudo ufw deny out from any to 192.168.178.200
sudo ufw deny out from any to 192.168.178.201
sudo ufw deny out from any to 192.168.178.202
sudo ufw deny out from any to 192.168.178.203
sudo ufw deny out from any to 192.168.178.204
sudo ufw deny out from any to 192.168.178.205
sudo ufw deny out from any to 192.168.178.206
sudo ufw deny out from any to 192.168.178.207
sudo ufw deny out from any to 192.168.178.208
sudo ufw deny out from any to 192.168.178.209
sudo ufw deny out from any to 192.168.178.210
sudo ufw deny out from any to 192.168.178.211
sudo ufw deny out from any to 192.168.178.212
sudo ufw deny out from any to 192.168.178.213
sudo ufw deny out from any to 192.168.178.214
sudo ufw deny out from any to 192.168.178.215
sudo ufw deny out from any to 192.168.178.216
sudo ufw deny out from any to 192.168.178.217
sudo ufw deny out from any to 192.168.178.218
sudo ufw deny out from any to 192.168.178.219
sudo ufw deny out from any to 192.168.178.220
sudo ufw deny out from any to 192.168.178.221
sudo ufw deny out from any to 192.168.178.222
sudo ufw deny out from any to 192.168.178.223
sudo ufw deny out from any to 192.168.178.224
sudo ufw deny out from any to 192.168.178.225
sudo ufw enable

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:

sudo ufw allow 51820/udp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload

Die richtigen DNS Einträge hatte ich zuvor schon erstellt. In der Fritzbox mache ich die Portweiterleitungen per "Freigaben":

Fritzbox Freigaben

Bei der Installation vertraue ich auf das Skript von fossorial.

cd /opt
sudo mkdir pangolin
cd pangolin
# wget command von Dokumentation
sudo ./installer
sudo usermod -aG docker $USER

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.

docker compose down  # die Docker container runterfahren
cd /opt
sudo btrfs subvolume create /opt/docker
sudo mv -r /opt/pangolin /opt/docker/pangolin
cd /opt/docker/pangolin 
docker compose up -d

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:

sudo btfrs subvolume list /opt/docker

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:

cd /opt/docker/pangolin
docker compose down
cd ..
sudo systemctl daemon-reload
sudo mount -a

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.

apt search btrbk
sudo apt install btrbk
cd /etc/btrbk/
sudo cp btrbk.conf.example btrbk.conf
sudo vim btrbk.conf
sudo mkdir /btrbk_snapshots
sudo btrbk run --dry-run
sudo btrbk run
ls /btrbk_snapshots/docker.20250628T1949/pangolin/

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.

sudo zfs create tank/backup/giga
sudo zfs set mountpoint=/tank/backup/giga tank/backup/giga
sudo useradd -m recvbtrbk
sudo chown -R recvbtrbk:root /tank/backup/giga
sudo su recvbtrbk
cd ~
mkdir ~/.ssh
echo "<Public key string>" >> ~/.ssh/authorized_keys

Auf giga muss ich den Private Key abspeichern und die btrbk.conf erweitern:

cd /etc/btrbk/
sudo mkdir ssh
cd ssh/
sudo vim btrbk # hier kopiere ich den Private Key ins File
sudo chmod 600 btrbk
sudo ssh -i /etc/btrbk/ssh/btrbk recvbtrbk@hpmicro # Test
cd ..
sudo vim btrbk.conf
sudo btrbk run --dry-run
sudo btrbk run
# 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
sudo systemctl daemon-reload
sudo systemctl enable btrbk.timer
sudo systemctl start btrbk.timer
systemctl status btrbk.timer

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]
        hourly = 36
        daily = 30
        monthly = 3
        yearly = 0

[template_backup]
        hourly = 30
        daily = 90
        monthly = 12
        yearly = 0

Das baue ich in /etc/btrbk/btrbk.conf nach:

#
# Meine retention policy:
#
snapshot_preserve_min   2d
snapshot_preserve       36h 30d 4w 3m 0y

target_preserve_min     10d
target_preserve         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