###Zaunpfahl###
Lt. Commander
- Registriert
- Jan. 2010
- Beiträge
- 1.685
Vorwort: Der folgende Artikel wurde überwiegend mithilfe von KI (Gemini 3.8 Thinking) erstellt. Dennoch entspricht er meiner Erfahrung, Absicht und Meinung.
Fertig-NAS-Systeme von QNAP glänzen ab Werk mit zahllosen Zusatzanwendungen, Multimedia-Paketen und bunten Web-Oberflächen. Wer sein NAS im privaten Homelab jedoch primär als verlässliches Datengrab, für Backups und als Docker-Host betreibt, zahlt für diesen Funktionsumfang einen hohen Preis: Hohe Grundlast, Telemetrie, proprietäre Software-Schichten und in der Vergangenheit immer wieder kritische Sicherheitslücken (wie Qlocker oder DeadBolt).
Hauptsächlich hat mich das OS und die am Ende doch halbgare Software mit ihren Eigenheiten und Limitierungen gestört.
Die Antwort auf dieses Problem lautet KISS (Keep It Simple, Stupid) in Verbindung mit einem immutablen (unveränderlichen) Betriebssystem: Flatcar Container Linux.
Die Kernideen hinter diesem Aufbau:
Das QNAP TS-453D bietet eine hervorragende Hardware-Grundlage für ein schlankes Linux-System:
Laufwerksaufteilung in den 4 Schächten:
Wichtig: Schutz des internen QNAP-DOM
Auf dem Mainboard des TS-453D sitzt ein fest verlöteter USB-DOM-Flashspeicher (4 GB) mit dem werksseitigen QNAP-Bootloader. Dieser Flash-Speicher wird bei der Installation nicht überschrieben oder angefasst. Flatcar wird vollständig auf der 250-GB-SSD in Schacht 1 installiert. Wird die SSD entfernt, bootet das Gerät wie am ersten Tag in den QNAP-Einrichtungsassistenten – der Wiederverkaufswert und die Reversibilität bleiben zu 100 % erhalten.
Außerdem würden sich die 4 GB nicht lohnen darauf das OS zu installieren. Meiner Meinung nach.
Vorbereitung im BIOS:
Flatcar wird beim allerersten Bootvorgang über sogenannte Ignition-Dateien (JSON) konfiguriert. Da JSON von Hand fehleranfällig ist, verwendet man das Übersetzungswerkzeug Butane und formuliert das System deklarativ in YAML (
Installation:
https://www.flatcar.org/docs/latest/deploy/bare-metal/installing-to-disk/
Das ist erst etwas ungewöhnlich aber ich finds am Ende ganz geil.
Man bootet auf dem System in irgendein Linux seiner wahl. Erstellt irgendwo die
Führt den Befehl aus für das entsprechende auserwählte OS Laufwerk.
Ggf. Bootreihenfolge anpassen und rein booten. Per ssh drauf und loslegen.
Die technischen Besonderheiten der Konfiguration:
Standardmäßig hat Flatcar Container Linux kein docker compose.
Auf einer herkömmlichen Linux-Distribution (wie Debian oder Fedora) lässt sich zusätzliche Hardware meist über Paketmanager oder DKMS nachrüsten. Unter Flatcar existieren weder Compiler noch Header im Systemabbild.
Das QNAP TS-453D verwendet zur Lüftersteuerung einen ITE IT8528 Embedded Controller (EC). Ohne das Out-of-Tree-Kernelmodul
Bauen im ephemeren Developer-Container
Flatcar stellt für jeden Release-Kanal ein passendes, signiertes Entwickler-Abbild bereit. Das Skript
Die KI hat mir hier auch die Kommentare ins deutsche übersetzt 😅, egal was solls 🤷♂️
Das Skript liefert zwei Kernel-Module im Ordner
Treiber-Injektion und CoolerControl per Docker Compose
Um den Flatcar-Host modifikationsfrei zu halten, übernimmt ein kleiner Alpine-Container (
Über die CoolerControl-Weboberfläche lässt sich der 120-mm-Gehäuselüfter des QNAP TS-453D nun abhängig von Festplatten- und CPU-Temperaturen regeln.
Da Flatcar bewusst minimal ausgeliefert wird, fehlt im Terminal selbst ein einfacher Texteditor wie
Um den modernen Terminal-Editor Micro bereitzustellen, ohne Binärdateien unkontrolliert ins System zu kopieren, übernimmt ein flüchtiger Alpine-Hilfscontainer die Installation nach
Ein einmaliges
Alle Anwendungsdienste sind in übersichtliche Projektordner unter
Caddy verwaltet alle internen Web-Oberflächen. Durch die Cloudflare-DNS-Challenge erhält das System vollwertige TLS-Zertifikate, ohne dass Portweiterleitungen am Internet-Router geöffnet werden müssen. Ein eigenes Dockerfile kompiliert Caddy mit dem nötigen DNS-Provider-Plugin.
Das Konzept und die Motivation
Der Hauptgrund für dieses Setup ist die Herstellung einer echten, validen SSL-Verbindung. Während man lokal theoretisch mit selbstsignierten Zertifikaten arbeiten könnte, führt dies oft zu Problemen: Moderne Betriebssysteme wie iOS und Android sind extrem strikt, was die Zertifikatsprüfung angeht. Besonders Hintergrundprozesse oder Apps verweigern oft den Dienst, wenn kein von einer anerkannten Zertifizierungsstelle (CA) signiertes Zertifikat vorliegt.
Um dies zu lösen, nutzen wir die DNS-01 Challenge. Anstatt dass der Zertifizierungsdienst den Server über Port 80/443 von außen prüfen muss (HTTP-01), beweist Caddy über die Cloudflare-API, dass die Domäne euch gehört, indem ein temporärer TXT-Record im DNS gesetzt wird.
Der große Vorteil: Ihr erhaltet ein "echtes" Zertifikat, das von jedem Gerät weltweit akzeptiert wird, ohne dass ihr einen einzigen Port in der Fritzbox nach außen öffnen müsst. Die Verbindung bleibt komplett intern, ist aber perfekt verschlüsselt.
Kosten für
Schritt-für-Schritt Einrichtung
1. Cloudflare DNS Konfiguration
Im Cloudflare-Dashboard wird für die Domäne ein A-Record angelegt, der auf die lokale LAN-IP des QNAP-NAS verweist (z. B.
* Wichtig: Der "Proxy"-Status (die orangefarbene Wolke) muss auf Disabled (DNS only) stehen, da Cloudflare nicht in euer lokales Netzwerk routen kann.
2. Fritzbox: DNS-Rebinding-Schutz deaktivieren
Die Fritzbox blockiert standardmäßig DNS-Antworten, die eine lokale IP zurückgeben, um sogenannte DNS-Rebinding-Angriffe zu verhindern. Damit die Domäne
3. Caddy mit Cloudflare-Plugin konfigurieren
Hinweis zum API-Token: Erstellt in Cloudflare einen Token mit der Berechtigung
Für die zentrale Übersicht und Steuerung der Stacks wird Arcane genutzt. Es liest die Compose-Dateien direkt aus dem Dateisystem aus und ermöglicht Container-Updates über eine aufgeräumte Web-UI.
Statt schwerfälliger Samba-Server mit Avahi- oder WSDD-Netzwerk-Broadcasts kommt ein schlankes
Dank
Als Backup-Lösung sichert Kopia die Daten des 8-TB-Hauptspeichers inkrementell und dedupliziert auf die interne 3-TB-Festplatte (
Der Wechsel von QTS zu Flatcar Container Linux auf dem TS-453D eliminiert alle Schwachstellen proprietärer Fertig-NAS-Betriebssysteme.
Die größten Vorteile in der Praxis:
Mögliche Hürden:
Das QNAP TS-453D erweist sich in dieser Kombination als leises, zuverlässiges und "nurDasWasIchBrauch" Homelab-Fundament.
Flatcar Container Linux auf dem QNAP TS-453D: Das unzerstörbare KISS-NAS
1. Einleitung, Motivation & Philosophie
Fertig-NAS-Systeme von QNAP glänzen ab Werk mit zahllosen Zusatzanwendungen, Multimedia-Paketen und bunten Web-Oberflächen. Wer sein NAS im privaten Homelab jedoch primär als verlässliches Datengrab, für Backups und als Docker-Host betreibt, zahlt für diesen Funktionsumfang einen hohen Preis: Hohe Grundlast, Telemetrie, proprietäre Software-Schichten und in der Vergangenheit immer wieder kritische Sicherheitslücken (wie Qlocker oder DeadBolt).
Hauptsächlich hat mich das OS und die am Ende doch halbgare Software mit ihren Eigenheiten und Limitierungen gestört.
Die Antwort auf dieses Problem lautet KISS (Keep It Simple, Stupid) in Verbindung mit einem immutablen (unveränderlichen) Betriebssystem: Flatcar Container Linux.
Die Kernideen hinter diesem Aufbau:
- Möglichst alles in Container: Einerseits kenn ich mich damit schon aus und im schlimmsten Fall lerne ich was hinzu. Dazu ist alles sehr schnell reproduzierbar. Also die ganze Software mit all seinen Einstellungen (copy&paste). Und zum großen Teil Hardware unabhängig. Also wenn man mal wechseln wollen würde. Ein paar Hardware Konfigs anpassen und das kann man dann auch der KI überlassen wenn man möchte, ist ja alles nur Text.
- Unveränderliches Basissystem: Das Root-Dateisystem ist schreibgeschützt montiert. Betriebssystem-Updates erfolgen atomar über zwei getrennte Partitionen (A/B-Schema) im Hintergrund. Schlägt ein Update fehl, startet der Host automatisch in die funktionierende Version zurück.
- Kein Konfigurationsdrift: Es gibt keinen klassischen Paketmanager (
apt,yum), der über Jahre fragmentieren oder zerschossen werden kann. Die gesamte Host-Konfiguration wird rein deklarativ über eine einzige Datei (Ignition/Butane) definiert. Das NAS ist in unter fünf Minuten auf jeder beliebigen x86-Hardware exakt identisch neu aufgesetzt. - Portabilität schlägt proprietäre Storage-Pools: Unter QTS liegen Daten oft in proprietären LVM- und Storage-Pool-Strukturen. Stirbt das QNAP-Mainboard, lassen sich die Platten an einem normalen PC kaum oder nur mit extremem Aufwand auslesen. In diesem Setup läuft jedes Laufwerk als eigenständiges Btrfs-Dateisystem. Geht die Hardware kaputt, zieht man die Festplatte heraus, steckt sie per SATA oder USB an jedes beliebige Linux-Notebook und hat sofort vollen Datenzugriff.
- Backups vor Redundanz: Im privaten Umfeld wird Ausfallsicherheit (RAID-Parität) oft mit Datensicherheit verwechselt. Ein RAID schützt lediglich vor Hardware-Ausfallzeiten, nicht vor versehentlichem Löschen, Dateisystemfehlern oder Ransomware. Die Devise lautet hier: Keine unnötige RAID-Komplexität, sondern strikte 3-2-1-Snapshot-Backups via Kopia auf getrennte interne und externe Datenträger.
2. Hardware-Basis & Vorbereitung
Das QNAP TS-453D bietet eine hervorragende Hardware-Grundlage für ein schlankes Linux-System:
- CPU: Intel Celeron J4125 (4 Kerne, 2,0 bis 2,7 GHz, x86_64, volles UEFI)
- Netzwerk: 2x 2,5 GbE (Intel I225-V)
- Video/I/O: HDMI-Port und USB-Ports für Tastatur und Konsolenausgabe
Laufwerksaufteilung in den 4 Schächten:
- Schacht 1: 250 GB SATA-SSD für das Flatcar-Basissystem und Root-Btrfs
- Schacht 2: 250 GB SATA-SSD (
toshiba_ssd) für Arbeitsdaten, Datenbanken und Anwendungs-Caches - Schacht 3: 8 TB HDD (
main_hdd_8tb) für die eigentlichen Hauptdaten (Medien, Dokumente, Fotos) - Schacht 4: 3 TB HDD (
backup_hdd_3tb) als lokales Ziel für versionierte Kopia-Snapshots
Wichtig: Schutz des internen QNAP-DOM
Auf dem Mainboard des TS-453D sitzt ein fest verlöteter USB-DOM-Flashspeicher (4 GB) mit dem werksseitigen QNAP-Bootloader. Dieser Flash-Speicher wird bei der Installation nicht überschrieben oder angefasst. Flatcar wird vollständig auf der 250-GB-SSD in Schacht 1 installiert. Wird die SSD entfernt, bootet das Gerät wie am ersten Tag in den QNAP-Einrichtungsassistenten – der Wiederverkaufswert und die Reversibilität bleiben zu 100 % erhalten.
Außerdem würden sich die 4 GB nicht lohnen darauf das OS zu installieren. Meiner Meinung nach.
Vorbereitung im BIOS:
- Monitor per HDMI und Tastatur per USB an das TS-453D anschließen.
- Beim Einschalten wiederholt [DEL] bzw. [F7] drücken, um das UEFI-BIOS aufzurufen.
- Die Boot-Reihenfolge so anpassen, dass die SATA-SSD vor dem internen QNAP-Flash-Speicher bootet.
3. Deklarative Bereitstellung via Butane & Ignition
Flatcar wird beim allerersten Bootvorgang über sogenannte Ignition-Dateien (JSON) konfiguriert. Da JSON von Hand fehleranfällig ist, verwendet man das Übersetzungswerkzeug Butane und formuliert das System deklarativ in YAML (
butane.yaml).Installation:
https://www.flatcar.org/docs/latest/deploy/bare-metal/installing-to-disk/
Das ist erst etwas ungewöhnlich aber ich finds am Ende ganz geil.
Man bootet auf dem System in irgendein Linux seiner wahl. Erstellt irgendwo die
ignition.json aus der butane.yaml.Führt den Befehl aus für das entsprechende auserwählte OS Laufwerk.
Ggf. Bootreihenfolge anpassen und rein booten. Per ssh drauf und loslegen.
Code:
variant: flatcar
version: 1.0.0
passwd:
users:
# 1. Admin-Benutzer (Passwort deaktiviert, reiner SSH-Key-Login)
- name: admin
groups:
- docker
- sudo
ssh_authorized_keys:
- "ssh-ed25519 AAAAC3Nz..."
# 2. Eingeschränkter Shutdown-Benutzer für Heimautomation
- name: shutdownuser
password_hash: "$6$saltsalt$hashgoeshere..."
storage:
directories:
- path: /mnt/usb
mode: 0755
filesystems:
# Host-SSD: Stateful-Root-Partition abfangen und als Btrfs formatieren
- device: /dev/disk/by-label/ROOT
format: btrfs
wipe_filesystem: true
label: ROOT
files:
# Automatische Reboots nach OS-Updates deaktivieren
- path: /etc/flatcar/update.conf
overwrite: true
contents:
inline: |
REBOOT_STRATEGY=off
# Wake-on-LAN für alle Interfaces aktivieren
- path: /etc/systemd/network/50-wol.link
mode: 0644
contents:
inline: |
[Match]
OriginalName=*
[Link]
WakeOnLan=magic
# Sudoers-Berechtigung für den Shutdown-Nutzer
- path: /etc/sudoers.d/shutdownuser
mode: 0440
contents:
inline: |
shutdownuser ALL=(ALL) NOPASSWD: /usr/bin/systemctl poweroff, /usr/bin/systemctl reboot, /usr/bin/whoami
# SSH-Einschränkung via ForceCommand
- path: /etc/ssh/sshd_config.d/shutdown.conf
mode: 0644
contents:
inline: |
Match User shutdownuser
PasswordAuthentication yes
ForceCommand /bin/sh -c 'case "$SSH_ORIGINAL_COMMAND" in *whoami*) sudo /usr/bin/whoami ;; *poweroff*|*shutdown*) sudo /usr/bin/systemctl poweroff ;; *reboot*) sudo /usr/bin/systemctl reboot ;; *) echo "Access denied"; exit 1 ;; esac'
# Skript für automatisches USB-Backup
- path: /root/run-backup.sh
mode: 0755
contents:
inline: |
#!/bin/bash
CONTAINER_NAME="kopia"
USB_DESTINATION="/mnt/usb/kopia-offline-sync"
echo "$(date): USB backup triggered."
# 1. Zielverzeichnis anlegen und Rechte setzen
mkdir -p "$USB_DESTINATION"
chown -R 1000:1000 "$USB_DESTINATION"
# 2. Snapshot-Sync aus Kopia anstoßen
docker exec $CONTAINER_NAME kopia repository sync-to filesystem --path "$USB_DESTINATION" || true
# 3. Puffer auf Platte schreiben
sync
# 4. Externe Festplatte sicher aushängen
systemctl stop mnt-usb.mount
echo "$(date): Backup finished and drive unmounted."
systemd:
units:
# Docker erst starten, wenn alle Speicher-Pools gemountet sind
- name: docker.service
enabled: true
dropins:
- name: wait-for-mounts.conf
contents: |
[Unit]
After=mnt-toshiba_ssd.mount mnt-main_hdd_8tb.mount mnt-backup_hdd_3tb.mount
Requires=mnt-toshiba_ssd.mount mnt-main_hdd_8tb.mount mnt-backup_hdd_3tb.mount
# Btrfs-Mount: Cache- und App-SSD
- name: mnt-toshiba_ssd.mount
enabled: true
contents: |
[Unit]
Description=Mount Toshiba SSD
After=local-fs.target
[Mount]
What=/dev/disk/by-label/toshiba_ssd
Where=/mnt/toshiba_ssd
Type=btrfs
Options=defaults,compress=zstd:1
[Install]
WantedBy=multi-user.target
# Btrfs-Mount: 8 TB Hauptdatenplatte
- name: mnt-main_hdd_8tb.mount
enabled: true
contents: |
[Unit]
Description=Mount Main 8TB HDD
After=local-fs.target
[Mount]
What=/dev/disk/by-label/main_hdd_8tb
Where=/mnt/main_hdd_8tb
Type=btrfs
Options=defaults,compress=zstd:1
[Install]
WantedBy=multi-user.target
# Btrfs-Mount: 3 TB interne Backup-Platte
- name: mnt-backup_hdd_3tb.mount
enabled: true
contents: |
[Unit]
Description=Mount Backup 3TB HDD
After=local-fs.target
[Mount]
What=/dev/disk/by-label/backup_hdd_3tb
Where=/mnt/backup_hdd_3tb
Type=btrfs
Options=defaults,compress=zstd:1
[Install]
WantedBy=multi-user.target
# Automount für externe USB-Festplatte mit Label "BACKUP"
- name: mnt-usb.mount
enabled: true
contents: |
[Unit]
Description=Automount USB Drive by Label
BindsTo=dev-disk-by\x2dlabel-BACKUP.device
After=dev-disk-by\x2dlabel-BACKUP.device
[Mount]
What=/dev/disk/by-label/BACKUP
Where=/mnt/usb
Type=btrfs
Options=defaults,noatime
[Install]
WantedBy=dev-disk-by\x2dlabel-BACKUP.device
# Automatischer Backup-Hook beim Einhängen der USB-Platte
- name: usb-hook.service
enabled: true
contents: |
[Unit]
Description=Trigger Kopia Backup on USB Mount
Requires=mnt-usb.mount docker.service
After=mnt-usb.mount docker.service
[Service]
Type=oneshot
TimeoutStartSec=infinity
ExecStart=/bin/bash /root/run-backup.sh
[Install]
WantedBy=mnt-usb.mount
Die technischen Besonderheiten der Konfiguration:
- Btrfs mit transparenter ZSTD-Kompression: Alle internen Datenträger werden als eigenständige Btrfs-Laufwerke mit dem Mount-Parameter
compress=zstd:1betrieben. Das spart Speicherplatz, schont die Schreibzyklen der SSDs und erhöht den sequenziellen Durchsatz komprimierbarer Daten. - Zuverlässige Startreihenfolge (Race-Condition-Schutz): Docker startet unter Linux sehr früh. Würden Container starten, bevor die Festplatten eingehängt sind, würde Docker die Mount-Pfade als leere Verzeichnisse auf der Boot-SSD anlegen. Durch den systemd Drop-In
wait-for-mounts.confwartet der Docker-Daemon zwingend, bis alle drei Partitionen erfolgreich eingebunden sind. - Gehärteter Shutdown-Benutzer: Um das NAS automatisiert (etwa über ein Home-Assistant-Dashboard oder eine USV) herunterzufahren, muss kein voller Root-SSH-Schlüssel hinterlegt werden. Der Nutzer
shutdownuserdarf sich per Passwort anmelden, wird jedoch im SSH-Daemon überForceCommandstrikt aufpoweroff,rebootundwhoamibeschränkt – alle anderen Befehle oder interaktive Shells werden abgewiesen. - Vorbereiteter USB-Backup-Hook: Beim Anschließen eines externen Datenträgers mit dem Label
BACKUPbindet systemd diesen automatisch nach/mnt/usbein und triggert denusb-hook.service. Das Skript stößt einen Repository-Sync in Kopia an, flusht alle Datenpuffer (sync) und unmountet die Platte sicher. (Praxis-Hinweis: Diese Automatisierung ist zwar sauber deklariert, funktioniert aber nicht, ich machs einfach händisch 🤷♂️ )
Standardmäßig hat Flatcar Container Linux kein docker compose.
Code:
mkdir -p ~/.docker/cli-plugins
curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose
chmod +x ~/.docker/cli-plugins/docker-compose
4. Das QNAP-Hardware-Problem: Lüftersteuerung & Kernel-Module
Auf einer herkömmlichen Linux-Distribution (wie Debian oder Fedora) lässt sich zusätzliche Hardware meist über Paketmanager oder DKMS nachrüsten. Unter Flatcar existieren weder Compiler noch Header im Systemabbild.
Das QNAP TS-453D verwendet zur Lüftersteuerung einen ITE IT8528 Embedded Controller (EC). Ohne das Out-of-Tree-Kernelmodul
qnap8528 sowie den im Standard-Flatcar-Kernel deaktivierten Treiber drivetemp kann Linux weder den Gehäuselüfter regeln noch Festplattentemperaturen über das Standard-Subsystem hwmon auslesen.Bauen im ephemeren Developer-Container
Flatcar stellt für jeden Release-Kanal ein passendes, signiertes Entwickler-Abbild bereit. Das Skript
build-drivers.sh lädt dieses Image passend zur exakten Host-Version (/etc/os-release) herunter, validiert die GPG-Signatur und startet den Build isoliert via systemd-nspawn.Die KI hat mir hier auch die Kommentare ins deutsche übersetzt 😅, egal was solls 🤷♂️
Bash:
#!/bin/bash
set -e
# 1. Absoluten Pfad ermitteln und Modulverzeichnis anlegen
BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DIR="$BASE_DIR/module"
mkdir -p "$DIR"
cd "$DIR"
# 2. Exakte LTS-Release-Version ermitteln
source /etc/os-release
URL="https://lts.release.flatcar-linux.net/amd64-usr/${VERSION_ID}/flatcar_developer_container.bin.bz2"
VER_FILE=".version"
# 3. Bereinigung: Veraltete Images nach OS-Updates löschen
if [ -f flatcar_developer_container.bin ]; then
if [ ! -f "$VER_FILE" ] || [ "$(cat "$VER_FILE")" != "$VERSION_ID" ]; then
echo "🔄 OS-Update erkannt! Lösche veralteten 1-GB-Container..."
rm -f flatcar_developer_container.bin "$VER_FILE"
else
echo "✅ Developer-Container passt zum aktuellen OS ($VERSION_ID)."
fi
fi
# 4. Download und Signatur-Verifikation
if [ ! -f flatcar_developer_container.bin ]; then
rm -rf flatcar_developer_container.bin.bz2* Flatcar_Image_Signing_Key.asc .gnupg_temp
echo "Lade Developer-Container für Flatcar LTS ${VERSION_ID}..."
curl -f -L --progress-bar "$URL" -o flatcar_developer_container.bin.bz2
echo "Lade GPG-Signatur und Flatcar-Schlüssel..."
curl -f -L -s "${URL}.sig" -o flatcar_developer_container.bin.bz2.sig
curl -f -L -s "https://www.flatcar.org/security/image-signing-key/Flatcar_Image_Signing_Key.asc" -o Flatcar_Image_Signing_Key.asc
export GNUPGHOME="$(pwd)/.gnupg_temp"
mkdir -p "$GNUPGHOME"
chmod 700 "$GNUPGHOME"
gpg --quiet --import Flatcar_Image_Signing_Key.asc
if ! gpg --verify flatcar_developer_container.bin.bz2.sig flatcar_developer_container.bin.bz2; then
echo "❌ FEHLER: Signaturprüfung fehlgeschlagen!"
rm -rf "$GNUPGHOME" flatcar_developer_container.bin.bz2* Flatcar_Image_Signing_Key.asc
exit 1
fi
echo "✅ Signatur verifiziert. Entpacke Image..."
rm -rf "$GNUPGHOME" Flatcar_Image_Signing_Key.asc flatcar_developer_container.bin.bz2.sig
bunzip2 flatcar_developer_container.bin.bz2
echo "$VERSION_ID" > "$VER_FILE"
fi
# 5. Build-Skript für den Developer-Container schreiben
cat << 'EOF' > compile.sh
#!/bin/bash
set -e
export FEATURES="-network-sandbox -ipc-sandbox -pid-sandbox"
echo "Hole Kernel-Quellen via Gentoo Portage..."
emerge-gitclone -q
emerge -gKv coreos-sources
zcat /proc/config.gz > /usr/src/linux/.config
# drivetemp in Kernelkonfiguration aktivieren
sed -i 's/# CONFIG_SENSORS_DRIVETEMP is not set/CONFIG_SENSORS_DRIVETEMP=m/' /usr/src/linux/.config
make -C /usr/src/linux olddefconfig
make -C /usr/src/linux modules_prepare
# Natives drivetemp-Modul kompilieren
echo "Kompiliere drivetemp..."
make -C /usr/src/linux M=drivers/hwmon KBUILD_MODPOST_WARN=1 modules
cp /usr/src/linux/drivers/hwmon/drivetemp.ko /host/
# qnap8528 EC-Treiber klonen und kompilieren
echo "Kompiliere qnap8528..."
cd /tmp
rm -rf qnap8528
git clone -q https://github.com/0xGiddi/qnap8528.git
cd qnap8528/src
make -C /usr/src/linux M=$(pwd) KBUILD_MODPOST_WARN=1 modules
cp qnap8528.ko /host/
echo "✅ Module erfolgreich gebaut!"
EOF
chmod +x compile.sh
# 6. Build im Container ausführen
echo "Starte Developer-Container via systemd-nspawn..."
sudo systemd-nspawn --quiet --bind="$DIR:/host" --image=flatcar_developer_container.bin /host/compile.sh
# 7. Aufräumen, um Speicherplatz freizugeben
sudo rm -f flatcar_developer_container.bin compile.sh
echo "🎉 Fertig. Module liegen in ./module/"
Das Skript liefert zwei Kernel-Module im Ordner
module/: qnap8528.ko und drivetemp.ko.Treiber-Injektion und CoolerControl per Docker Compose
Um den Flatcar-Host modifikationsfrei zu halten, übernimmt ein kleiner Alpine-Container (
driver-modules-loader) beim Booten das Laden der Module in den Host-Kernel (SYS_MODULE). Erst wenn dieser Container über seinen Healthcheck meldet, dass qnap8528 geladen ist, startet der CoolerControl-Daemon.
Code:
services:
# ---------------------------------------------------------
# SERVICE 1: Kernel-Modul-Loader (Einmalig beim Boot)
# ---------------------------------------------------------
driver-modules-loader:
container_name: driver-modules-loader
image: alpine:latest
restart: unless-stopped
cap_drop:
- ALL
cap_add:
- SYS_MODULE
volumes:
- ./module:/module:ro
- /lib/modules:/lib/modules:ro
healthcheck:
test: ["CMD-SHELL", "grep -q qnap8528 /proc/modules"]
interval: 5s
timeout: 2s
retries: 3
start_period: 5s
command:
- /bin/sh
- -c
- |
echo "Injecting qnap fan driver..."
if ! lsmod | grep -q qnap8528; then
insmod /module/qnap8528.ko skip_hw_check=true || { echo "Failed to load qnap8528"; exit 1; }
fi
echo "Injecting HDD temperature drivers..."
if ! lsmod | grep -q drivetemp; then
insmod /module/drivetemp.ko || echo "Failed to load drivetemp."
fi
echo "✅ Kernel modules ready."
sleep infinity
# ---------------------------------------------------------
# SERVICE 2: CoolerControl Web-UI & Daemon
# ---------------------------------------------------------
coolercontrol:
container_name: coolercontrol
image: coolercontrol/coolercontrold:latest
restart: unless-stopped
depends_on:
driver-modules-loader:
condition: service_healthy
cap_add:
- SYS_RAWIO
- SYS_ADMIN
security_opt:
- apparmor:unconfined
devices:
- /dev/sda:/dev/sda
- /dev/sdb:/dev/sdb
- /dev/sdc:/dev/sdc
- /dev/sdd:/dev/sdd
volumes:
- /sys:/sys
- /dev:/dev
- /run/udev:/run/udev:ro
- ./cc-config:/etc/coolercontrol
networks:
- caddy_rp_net
networks:
caddy_rp_net:
external: true
Über die CoolerControl-Weboberfläche lässt sich der 120-mm-Gehäuselüfter des QNAP TS-453D nun abhängig von Festplatten- und CPU-Temperaturen regeln.
5. CLI-Komfort ohne Paketmanager: Der Editor Micro
Da Flatcar bewusst minimal ausgeliefert wird, fehlt im Terminal selbst ein einfacher Texteditor wie
nano (lediglich vi ist vorhanden).Um den modernen Terminal-Editor Micro bereitzustellen, ohne Binärdateien unkontrolliert ins System zu kopieren, übernimmt ein flüchtiger Alpine-Hilfscontainer die Installation nach
/opt/bin (einem der wenigen beschreibbaren Pfade im Flatcar-Suchpfad).micro/.env
micro/compose.yaml
Bash:
VERSION=2.0.15
micro/compose.yaml
Code:
services:
install-micro:
image: alpine:latest
environment:
- VERSION=${VERSION}
volumes:
- /opt/bin:/opt/bin
command:
- /bin/sh
- -c
- |
set -e
apk add --no-cache curl tar
cd /tmp
FILE="micro-$${VERSION}-linux64.tar.gz"
echo "Lade Micro v$$VERSION herunter..."
curl -sLO "https://github.com/micro-editor/micro/releases/download/v$${VERSION}/$$FILE"
curl -sLO "https://github.com/micro-editor/micro/releases/download/v$${VERSION}/$${FILE}.sha"
EXPECTED=$$(cat "$${FILE}.sha" | sed 's/sha256://' | cut -d' ' -f1)
ACTUAL=$$(sha256sum "$$FILE" | cut -d' ' -f1)
if [ "$$EXPECTED" != "$$ACTUAL" ] || [ -z "$$EXPECTED" ]; then
echo "FATAL: Checksum mismatch or file not found!"
exit 1
fi
echo "Prüfsumme ($$ACTUAL) verifiziert. Installiere nach /opt/bin..."
tar -xzf "$$FILE" -C /opt/bin --strip-components=1 "micro-$${VERSION}/micro"
echo "Installation abgeschlossen."
Ein einmaliges
docker compose up lädt das passende Release herunter, prüft den SHA256-Hash und legt das Binary in /opt/bin/micro ab – danach steht der Editor in jeder SSH-Session bereit.6. Der Anwendungs-Stack: Containerisierte Dienste
Alle Anwendungsdienste sind in übersichtliche Projektordner unter
/projects/ strukturiert und über ein gemeinsames Docker-Netzwerk (caddy_rp_net) verbunden.6.1 Reverse-Proxy mit automatischer Wildcard-DNS-Challenge (Caddy)
Caddy verwaltet alle internen Web-Oberflächen. Durch die Cloudflare-DNS-Challenge erhält das System vollwertige TLS-Zertifikate, ohne dass Portweiterleitungen am Internet-Router geöffnet werden müssen. Ein eigenes Dockerfile kompiliert Caddy mit dem nötigen DNS-Provider-Plugin.
Das Konzept und die Motivation
Der Hauptgrund für dieses Setup ist die Herstellung einer echten, validen SSL-Verbindung. Während man lokal theoretisch mit selbstsignierten Zertifikaten arbeiten könnte, führt dies oft zu Problemen: Moderne Betriebssysteme wie iOS und Android sind extrem strikt, was die Zertifikatsprüfung angeht. Besonders Hintergrundprozesse oder Apps verweigern oft den Dienst, wenn kein von einer anerkannten Zertifizierungsstelle (CA) signiertes Zertifikat vorliegt.
Um dies zu lösen, nutzen wir die DNS-01 Challenge. Anstatt dass der Zertifizierungsdienst den Server über Port 80/443 von außen prüfen muss (HTTP-01), beweist Caddy über die Cloudflare-API, dass die Domäne euch gehört, indem ein temporärer TXT-Record im DNS gesetzt wird.
Der große Vorteil: Ihr erhaltet ein "echtes" Zertifikat, das von jedem Gerät weltweit akzeptiert wird, ohne dass ihr einen einzigen Port in der Fritzbox nach außen öffnen müsst. Die Verbindung bleibt komplett intern, ist aber perfekt verschlüsselt.
Kosten für
[acht Ziffern].xyz => ~ 0.80 € im Jahr. Auch wenn eine Webdomäne für rein lokale Zwecke totaler Blödsinn ist. Ist es leider die einzig richtig und so vorhergesehen Lösung für echte verschlüsselte HTTP Verbindungen. Und der Preis ist wirklich schmerzfrei.Schritt-für-Schritt Einrichtung
1. Cloudflare DNS Konfiguration
Im Cloudflare-Dashboard wird für die Domäne ein A-Record angelegt, der auf die lokale LAN-IP des QNAP-NAS verweist (z. B.
192.168.178.20). * Wichtig: Der "Proxy"-Status (die orangefarbene Wolke) muss auf Disabled (DNS only) stehen, da Cloudflare nicht in euer lokales Netzwerk routen kann.
2. Fritzbox: DNS-Rebinding-Schutz deaktivieren
Die Fritzbox blockiert standardmäßig DNS-Antworten, die eine lokale IP zurückgeben, um sogenannte DNS-Rebinding-Angriffe zu verhindern. Damit die Domäne
12345678.xyz im LAN aufgelöst werden kann, müsst ihr eine Ausnahme hinzufügen:- Gehe zu
Heimnetz$\rightarrow$Netzwerk$\rightarrow$Netzwerkeinstellungen. - Suche den Bereich DNS-Rebinding-Schutz.
- Trage dort deine Domäne ein (z. B.
12345678.xyzund*.12345678.xyz).
3. Caddy mit Cloudflare-Plugin konfigurieren
Hinweis zum API-Token: Erstellt in Cloudflare einen Token mit der Berechtigung
Zone:DNS:Edit, damit Caddy die TXT-Records für die Zertifikatsprüfung selbstständig setzen und löschen kann.caddy/Dockerfile
caddy/compose.yaml
caddy/Caddyfile
Code:
FROM caddy:builder-alpine AS builder
RUN xcaddy build --with github.com/caddy-dns/cloudflare
FROM caddy:alpine
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
caddy/compose.yaml
Code:
services:
caddy:
container_name: caddy
build: .
ports:
- 80:80
- 443:443
environment:
- CLOUDFLARE_API_TOKEN=abcdefg
- TZ=Europe/Berlin
volumes:
- ./data:/data
- ./config:/config
- ./Caddyfile:/etc/caddy/Caddyfile
restart: always
networks:
- rp_net
networks:
rp_net:
name: caddy_rp_net
caddy/Caddyfile
Code:
*.12345678.xyz {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
@arcane host arcane.12345678.xyz
handle @arcane {
reverse_proxy arcane:3552
}
@beszel host beszel.12345678.xyz
handle @beszel {
reverse_proxy beszel:8090
}
@scrutiny host scrutiny.12345678.xyz
handle @scrutiny {
reverse_proxy scrutiny:8080
}
@coolercontrol host coolercontrol.12345678.xyz
handle @coolercontrol {
reverse_proxy coolercontrol:11987
}
@immich host immich.12345678.xyz
handle @immich {
reverse_proxy immich:2283
}
@kopia host kopia.12345678.xyz
handle @kopia {
reverse_proxy kopia:51515
}
}
6.2 Docker-Verwaltung mit Arcane
Für die zentrale Übersicht und Steuerung der Stacks wird Arcane genutzt. Es liest die Compose-Dateien direkt aus dem Dateisystem aus und ermöglicht Container-Updates über eine aufgeräumte Web-UI.
Code:
services:
arcane:
image: ghcr.io/getarcaneapp/manager:latest
container_name: arcane
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/arcane/data:/app/data
- /opt/arcane/projects:/app/data/projects
environment:
- APP_URL=http://localhost:3552
- PUID=1000
- PGID=1000
- ENCRYPTION_KEY=123
- JWT_SECRET=123
cgroup: host
restart: always
networks:
- caddy_rp_net
networks:
caddy_rp_net:
external: true
6.3 Gehärtetes High-Performance Samba
Statt schwerfälliger Samba-Server mit Avahi- oder WSDD-Netzwerk-Broadcasts kommt ein schlankes
smbd-only-Image zum Einsatz.Dank
network_mode: host, Beschränkung auf SMB3, aktivierter SMB-Verschlüsselung, Multi-Channel-Unterstützung und asynchronem I/O (aio_read_size=1, aio_write_size=1) lastet das Setup die 2,5-GbE-Netzwerkkarte ohne CPU-Engpässe aus.
Code:
services:
samba:
image: ghcr.io/servercontainers/samba:smbd-only-latest
container_name: smb
restart: unless-stopped
network_mode: host
environment:
# Protokoll-Sicherheit
- SAMBA_GLOBAL_CONFIG_min_SPACE_protocol=SMB3
- SAMBA_GLOBAL_CONFIG_smb_SPACE_encrypt=required
# Durchsatz-Tuning für 2.5 GbE
- SAMBA_GLOBAL_CONFIG_server_SPACE_multi_SPACE_channel_SPACE_support=yes
- SAMBA_GLOBAL_CONFIG_aio_SPACE_read_SPACE_size=1
- SAMBA_GLOBAL_CONFIG_aio_SPACE_write_SPACE_size=1
# Zugangsdaten & Rechte
- ACCOUNT_sambu=abcd
- UID_sambu=1000
# Freigabedefinition
- "SAMBA_VOLUME_CONFIG_mystorage=[media]; path=/data; valid users = sambu; guest ok = no; read only = no; browseable = yes"
volumes:
- /mnt/main_hdd_8tb/media:/data
6.4 Monitoring & Festplattenzustand: Beszel & Scrutiny
- Beszel: Extrem schlankes Host-Monitoring. Der Agent liest Systemmetriken und via
smartctldie Festplattenzustände aus. - Scrutiny: Führt über
SYS_RAWIOtiefgehende SMART-Analysen der vier physischen Laufwerke (/dev/sdabis/dev/sdd) durch und visualisiert Trends.
beszel/compose.yaml
scrunity/compose.yaml
Code:
services:
beszel:
image: henrygd/beszel:latest
container_name: beszel_hub
restart: unless-stopped
volumes:
- ./data:/beszel_data
environment:
- APP_URL=https://beszel.12345678.xyz
networks:
- caddy_rp_net
beszel-agent:
image: henrygd/beszel-agent:alpine
container_name: beszel_agent
restart: unless-stopped
network_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /:/extra-filesystems:ro
environment:
- PORT=45876
- KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5..."
networks:
caddy_rp_net:
external: true
scrunity/compose.yaml
Code:
services:
scrutiny:
image: ghcr.io/analogj/scrutiny:latest-omnibus
container_name: scrutiny
restart: unless-stopped
cap_add:
- SYS_RAWIO
devices:
- /dev/sda:/dev/sda
- /dev/sdb:/dev/sdb
- /dev/sdc:/dev/sdc
- /dev/sdd:/dev/sdd
volumes:
- /run/udev:/run/udev:ro
- ./config:/opt/scrutiny/config
- ./influxdb:/opt/scrutiny/influxdb
networks:
- caddy_rp_net
networks:
caddy_rp_net:
external: true
6.5 Synchronisation & Medien: Syncthing & Immich
- Syncthing: Synchronisiert Arbeitsordner kontinuierlich zwischen Clients und dem 8-TB-Pool.
- Immich: Leistungsfähige, selbstgehostete Foto- und Videoverwaltung mit PostgreSQL, pgvector-Erweiterung und Redis.
syncthing/compose.yaml
immich/compose.yaml
Code:
services:
syncthing:
container_name: syncthing
image: lscr.io/linuxserver/syncthing:latest
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
volumes:
- ./config:/config
- /mnt/main_hdd_8tb/syncthing_data:/data
ports:
- 22000:22000/tcp
- 22000:22000/udp
- 21027:21027/udp
restart: unless-stopped
healthcheck:
test: curl -fkLsS -m 2 127.0.0.1:8384/rest/noauth/health | grep -o --json OK || exit 1
interval: 10s
timeout: 2s
retries: 3
networks:
- caddy_rp_net
networks:
caddy_rp_net:
external: true
immich/compose.yaml
Code:
name: immich
services:
immich:
container_name: immich
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
volumes:
- ${UPLOAD_LOCATION}:/data
- ${THUMB_LOCATION}:/data/thumbs
- ${PROFILE_LOCATION}:/data/profile
env_file:
- .env
depends_on:
- redis
- database
restart: always
networks:
- caddy_rp_net
machine-learning:
container_name: immich_machine_learning
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
restart: always
volumes:
- ./model-cache:/cache
env_file:
- .env
networks:
- caddy_rp_net
redis:
container_name: immich_redis
image: docker.io/valkey/valkey:8-alpine
restart: always
healthcheck:
test: ["CMD-SHELL", "redis-cli ping || exit 1"]
networks:
- caddy_rp_net
database:
container_name: immich_postgres
image: ghcr.io/immich-app/image-postgresql:14-vector-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
volumes:
- ./postgres:/var/lib/postgresql/data
restart: always
networks:
- caddy_rp_net
networks:
caddy_rp_net:
external: true
6.6 Datensicherung mit Kopia
Als Backup-Lösung sichert Kopia die Daten des 8-TB-Hauptspeichers inkrementell und dedupliziert auf die interne 3-TB-Festplatte (
/mnt/backup_hdd_3tb).
Code:
services:
kopia:
image: kopia/kopia:latest
container_name: Kopia
hostname: kopia
restart: unless-stopped
command:
- server
- start
- --insecure
- --address=0.0.0.0:51515
- --server-username=${WEB_USER}
- --server-password=${WEB_PASS}
volumes:
- ./config:/app/config
- ./cache:/app/cache
- ./logs:/app/logs
- /mnt/main_hdd_8tb:/data/main:ro
- /mnt/backup_hdd_3tb:/data/backup
- /mnt/usb:/mnt/usb
networks:
- caddy_rp_net
networks:
caddy_rp_net:
external: true
7. Fazit & Erfahrungen aus dem Produktivbetrieb
Der Wechsel von QTS zu Flatcar Container Linux auf dem TS-453D eliminiert alle Schwachstellen proprietärer Fertig-NAS-Betriebssysteme.
Die größten Vorteile in der Praxis:
- Bootet viel Schneller: Etwas mehr als eine Minute und alles ist oben. Davor waren es 5 Minuten oder sogar mehr.
- Konfiguration ist punktgenau und reproduzierbar: Ich bin auch ein großer Freund von GUIs, weil sie implizit schon erklären was wie geht ohne eine Anleitung studieren zu müssen. Aber jetzt mit KI und wenn man es auch nur einmal einrichten möchte und dann soll es für längere Zeit so bleiben. Viel besser. QTS ist oft, zu oft GUI mößig einfach nur 💩 und überladen.
- Schlank: QTS ist und ok weil es auch möglichts viel abdecken möchte... sehr aufgeblasen. Habe ich nie gebraucht. Jetzt Installiere ich nur genau das was ich brauche.
- Volle Datenunabhängigkeit: Sollte das QNAP-Mainboard jemals den Geist aufgeben, nimmt man die Festplatten heraus und schließt sie per SATA oder USB an jedes x-beliebige Linux-Gerät an. Kein proprietärer QNAP-LVM-Layer, kein Herumschlagen mit QuTS hero oder fremden Software-RAIDs.
- Backup vor Redundanz: Für ein privates Homelab bietet die Kombination aus Kopia-Snapshots auf eine getrennte interne Platte und gelegentlichen Offline-Syncs auf externe Datenträger eine deutlich höhere Datensicherheit als ein klassisches RAID, das lediglich Verfügbarkeit garantiert.
- Keine Wartung, kein Zerfall: Das System bootet in Sekunden, belegt kaum Arbeitsspeicher und verändert seinen Host-Zustand nie. Sämtliche Applikationen sind strikt gekapselt.
Mögliche Hürden:
- Kein Klick-Interface für Speicher-Pools: Wer Festplatten per Drag-and-Drop im Browser partitionieren möchte, wird mit Flatcar nicht glücklich.
- Kernel-Updates: Wenn Flatcar auf einen neuen Kernel-LTS-Zweig wechselt, muss das Skript
build-drivers.sheinmal angestoßen werden, um die Treiberqnap8528.kounddrivetemp.kogegen den neuen Kernel zu bauen.
Das QNAP TS-453D erweist sich in dieser Kombination als leises, zuverlässiges und "nurDasWasIchBrauch" Homelab-Fundament.
Zuletzt bearbeitet: