← Übersicht  ·  Skripte & Dateien  ·  Heimnetz-Setup · OpenShift auslagern

OKD Single-Node auf Hetzner

Ein ganzes OpenShift (OKD 4) auf einem einzigen Cloud-Server – mit Web-Console, Router, interner Registry und allen Cluster-Operatoren. Der Laptop bleibt reiner oc-Client. scripts/sno.ps1 macht die Installation, Abrechnung stundenweise: Server loeschen = Kosten weg.

Stand: 1. September 2026 Installation ~30–45 min Windows-Laptop + WSL2 (Windows Subsystem for Linux 2) → Hetzner Cloud

Platzhalter einsetzen – werden in allen Befehlen unten ersetzt, nur im Browser, nichts wird gesendet

Das Prinzip

OKD (OpenShift Kubernetes Distribution) ist das Community-OpenShift. Single-Node (SNO) packt Control-Plane und Workload auf einen Server: API (Application Programming Interface) auf 6443, Web-Console + Router auf 443, interne Registry, alle Cluster-Operatoren. Dein oc auf dem Laptop (Superset von kubectl) spricht ueber HTTPS (Hypertext Transfer Protocol Secure) mit dem API-Server. Die Installation selbst laeuft in WSL2openshift-install gibt es nur fuer Linux.

Dein Laptop oc · hcloud CLI (Command-Line Interface) · WSL2 (openshift-install)
steuert nur, rechnet nicht
Hetzner-Server SCOS (CentOS Stream CoreOS) + OKD 4: API + Console + Router + Registry
alle Pods laufen hier
Blauer Streifen = auf deinem Laptop (PowerShell / WSL) Roter Streifen = auf dem Server (Rescue / SCOS)
Warum das aufwendiger ist als k3s

OKD-Nodes brauchen SCOS (CentOS Stream CoreOS) – ein unveraenderliches Container-OS, das der Machine Config Operator selbst verwaltet. Ein normales Ubuntu/CentOS taugt nicht.

Hetzner Cloud hat keinen SCOS-Katalogeintrag und erlaubt keinen Image-Upload. Loesung: vom Rescue-System per kexec in die SCOS-Live-Umgebung springen (Ignition als /config.ign ins initramfs eingebettet). Die Live-Umgebung fuehrt dann bootstrap-in-place aus: temporaere Control-Plane → coreos-installer schreibt SCOS + fertige Ignition auf /dev/sda → Reboot in den fertigen Node.

Das macht alles scripts/sno.ps1 – du musst den Ablauf nicht von Hand fahren (Abschnitt 7 zeigt ihn trotzdem).

Loest MicroShift ab

Die fruehere MicroShift-Fassung ist raus: ihr Installations-Repo (@redhat-et/microshift-COPR) liefert fuer CentOS Stream 9 nur noch einen Build von 2022 mit unaufloesbaren Abhaengigkeiten. MicroShift wird heute praktisch nur noch auf RHEL (Red Hat Enterprise Linux) 9.4+ mit Subscription unterstuetzt – und Hetzner hat kein RHEL-Image. OKD SNO (Single-Node OpenShift) deckt denselben Zweck ab (voller oc-Workflow, Web-Console) und wird gepflegt.

01 Voraussetzungen

Der einmalige Teil steht in Server-Basis: Hetzner-Konto + API-Token, hcloud CLI mit Kontext, SSH-Key bei Hetzner hinterlegt, oc/kubectl im PATH (Programmsuchpfad).

zusaetzlich WSL2

openshift-install laeuft nur unter Linux. Einmalig eine WSL2-Distribution installieren (Neustart noetig):

Laptop · PowerShell (als Admin)
wsl --install -d Ubuntu
wsl -l -v            # muss die Distro mit VERSION 2 zeigen
  • sno.ps1 holt openshift-install + oc selbst in die WSL (nach ~/okd-sno/bin) – nichts weiter einzurichten.
  • Der WSL-Traffic geht ueber dieselbe oeffentliche IP (Internet Protocol) wie der Laptop – die Firewall-Regel fuer 6443 passt also automatisch.
  • Server-Groesse: das offizielle SNO-Minimum ist 8 vCPU / 16 GB (cpx42) – das reicht fuer OKD leer (OKD belegt selbst schon ~11 GB RAM, CPU ist nie das Thema). sno.ps1 nimmt darum standardmaessig cpx52 (12 vCPU / 24 GB), damit auch eigene Workloads Platz haben. ccx33 (8 dediziert / 32 GB) laeuft am ruhigsten. Konkrete Auslastung mit einem ganzen Projekt-Stack: Bibliothek-Enterprise per Jenkins auf OKD.

02 Installation mit sno.ps1

Ein Befehl. Danach ~30–45 min warten (die ersten ~5 min mit Rueckfragen bei neuem SSH-Host-Key, der Rest laeuft durch). Server, Rescue, Ignition, kexec, wait-for – alles automatisch.

Laptop · PowerShell (im Repo-Wurzelverzeichnis)
.\scripts\sno.ps1 install
# Vorgabe: -Type cpx52 (12 vCPU / 24 GB)
# optional: -Type cpx42   (16 GB, nur OKD leer)  |  -Type ccx33 (dediziert, ruhiger)
#           -Location fsn1  (anderer Standort)

Was das Skript der Reihe nach tut:

  • neueste okd-scos-Version ermitteln, openshift-install+oc in die WSL holen
  • Firewall okd-sno-fw (22+6443 nur von deiner IP, 80+443 offen) und eine reservierte Primary-IPv4 okd-sno-ip anlegen – die feste IP ist wichtig, weil die API-Zertifikate und die nip.io-Namen an ihr haengen
  • Server okd-sno (cpx52, Ubuntu als Rescue-Traeger) erstellen, Rescue aktivieren, Hard-Reset (ein Soft-Reboot geht nicht in den Rescue)
  • in der WSL die install-config.yaml + single-node-ignition-config erzeugen (mit der festen IP: baseDomain: <ip>.nip.io, Pull-Secret ist ein Dummy – OKD braucht kein echtes)
  • Ignition + sno-kexec.sh ins Rescue hochladen, dort SCOS-Live-Kernel/initramfs holen, Ignition als /config.ign (xz-cpio) anhaengen, kexec
  • vom Laptop aus openshift-install wait-for install-complete gegen api.sno.<ip>.nip.io:6443
  • kubeconfig nach ~\.kube\okd-sno-hetzner.yaml kopieren, KUBECONFIG setzen, Console-URL + kubeadmin-Passwort ausgeben
Wenn es haengt

Der haeufigste Punkt ist zu wenig RAM (auf cpx42) waehrend der Operator-Konvergenz. oc get clusterversion / oc get co zeigen den Stand. Oft laeuft es mit ein paar Minuten mehr doch durch. Sonst .\scripts\sno.ps1 destroy und mit dem groesseren Vorgabe-Typ (cpx52) oder -Type ccx33 neu.

Bricht install direkt nach „kexec … ausloesen“ ab, laeuft der Server trotzdem schon los. Pruefen: ssh core@<ip> – zeigt es „CentOS Stream CoreOS“, dann nicht neu installieren, sondern nur weiterwarten: wsl -e bash scripts/sno-wsl.sh wait und danach … sno-wsl.sh finish /mnt/c/Users/<du>/.kube/okd-sno-hetzner.yaml.

03 Was dabei passiert (zum Mitlesen)

Waehrend wait-for laeuft, kannst du dem Node zusehen – .\scripts\sno.ps1 ssh (User core).

Phaseca.woran erkennbar
SCOS-Live bootet, node-image-pull + release-image~5–10 minssh core@… geht, OS (Betriebssystem) = „CentOS Stream CoreOS“
bootkube – temporaere Control-Plane, rendert die Master-Ignition~6 minjournalctl -b -u bootkube -f
install-to-diskcoreos-installer schreibt /dev/sda, dann Reboot~1 minSSH (Secure Shell) bricht ab
Reboot in den Node, bootstrap-in-place-post-reboot: CSRs, alle Operatoren, CVO~20–30 minAPI 6443 antwortet, oc get co

Der Node rebootet dabei ein- bis zweimal selbst (Machine Config Operator). Das ist normal.

04 Cluster pruefen

Laptop · PowerShell
$env:KUBECONFIG = "$env:USERPROFILE\.kube\okd-sno-hetzner.yaml"
oc whoami                 # -> system:admin
oc get clusterversion     # AVAILABLE=True, "Cluster version is 4.x.x-okd-scos.y"
oc get co                 # alle 34 auf AVAILABLE=True
oc get nodes -o wide      # 1 Node, ROLES control-plane,master,worker, cri-o
Web-Console

.\scripts\sno.ps1 console oeffnet https://console-openshift-console.apps.sno.<ip>.nip.io und zeigt Login kubeadmin + Passwort (aus ~/okd-sno/cfg/auth/kubeadmin-password in der WSL). Zertifikat ist selbstsigniert – im Browser einmal bestaetigen.

KUBECONFIG dauerhaft: hat install schon per SetEnvironmentVariable(…,'User') gesetzt – in neuen Terminals aktiv. Zuruecksetzen macht sno.ps1 down / destroy.

05 Erstes Projekt, App & Route

Normaler OpenShift-Workflow – wie im grossen Cluster.

Laptop · PowerShell (→ OKD)
oc new-project demo
oc new-app --image=docker.io/library/nginx --name=web
oc expose service/web                          # -> Route auf *.apps.sno.<ip>.nip.io
oc get route
# -> http://web-demo.apps.sno.<ip>.nip.io  (Port 80/443 sind fuer alle offen)
  • S2I-Builds, BuildConfig, DeploymentConfig, SCCs (Security Context Constraints), oc adm – alles da, es ist ein echtes OpenShift.
  • Auto-Deploy: Manifeste in Git + Argo CD (Continuous Delivery)/Flux (OLM/OperatorHub ist installiert).
  • Ganzes Projekt per CI (Continuous Integration): Bibliothek-Enterprise per Jenkins auf OKD – Jenkins baut die Images, pusht in die interne Registry, deployt per Helm (jede Falle dokumentiert).
  • Privat statt Route: oc port-forward svc/web 8080:80.

06 Kosten steuern: down / up

Preise sind Richtwerte. Hetzner rechnet stundenweise ab, solange der Server existiert – herunterfahren spart nichts, nur loeschen. Aktuelle Preise + Rechner: Kosten & Budget.

Laptop · PowerShell
.\scripts\sno.ps1 down       # kubelet stoppen -> Snapshot -> Server loeschen (IP + Snapshot bleiben)
.\scripts\sno.ps1 up         # Server aus dem Snapshot, gleiche feste IP -> Cluster kommt zurueck
.\scripts\sno.ps1 status     # laeuft er? seit wann? ~Kosten? API erreichbar?
.\scripts\sno.ps1 destroy    # alles weg: Server, Snapshot, reservierte IP, Firewall
Posten€/Std€/Tag€/Monat
Server CPX52 (12 vCPU / 24 GB, Vorgabe), waehrend er laeuft~0,138~3,30~100
Server CPX42 (16 GB, nur OKD leer), waehrend er laeuft~0,095~2,28~69
reservierte Primary-IP (bleibt bei down)~0,0008~0,02~0,60
Snapshot des konvergierten Node (~10 GB, bleibt bei down)~0,005~0,15

Nach down laufen nur IP + Snapshot weiter (~0,75 €/Monat). Eine 3-h-Session auf cpx52 ist ~40 Cent. Warum die feste IP: API-Zertifikate und alle nip.io-Namen sind auf die IP ausgestellt – mit einer neuen IP muesste man neu installieren.

Ein Klick / naechtliches Auto-Down

.\scripts\create-shortcuts.ps1 -For sno legt Desktop-Icons OKD-SNO-up/-down/-status an. .\scripts\install-autodown.ps1 -For sno loescht den Node jede Nacht um 03:00 (Windows-Aufgabenplanung). Der wichtigste Kostenschutz, weil Hetzner kein Ausgabenlimit hat. Voller Kill-Switch-Ueberblick: Server-Snapshot & Restore.

+ Manuell (ohne sno.ps1)

Falls du den Ablauf verstehen oder anpassen willst. Die drei Bausteine im Repo: scripts/sno.ps1 (Orchestrierung), scripts/sno-wsl.sh (Linux-Teil), scripts/sno-kexec.sh (Rescue-Teil).

1 WSL: Werkzeuge + Ignition

WSL2 · bash
W=~/okd-sno; mkdir -p $W/bin; cd $W
OKD=4.22.0-okd-scos.8   # neueste: github.com/okd-project/okd/releases/latest
curl -L https://github.com/okd-project/okd-scos/releases/download/$OKD/openshift-install-linux-$OKD.tar.gz | tar xz -C bin
curl -L https://github.com/okd-project/okd-scos/releases/download/$OKD/openshift-client-linux-$OKD.tar.gz  | tar xz -C bin oc kubectl
Datei · ~/okd-sno/cfg/install-config.yaml
apiVersion: v1
baseDomain: <ip>.nip.io
metadata:
  name: sno
compute:
- { name: worker, replicas: 0 }
controlPlane:
  name: master
  replicas: 1
networking:
  networkType: OVNKubernetes
  machineNetwork:
  - cidr: <ip>/32
platform:
  none: {}
bootstrapInPlace:
  installationDisk: /dev/sda
pullSecret: '{"auths":{"fake":{"auth":"aWQ6cGFzcwo="}}}'
sshKey: |
  <dein id_ed25519.pub>
WSL2 · bash
bin/openshift-install --dir cfg create single-node-ignition-config
# -> cfg/bootstrap-in-place-for-live-iso.ign  (+ cfg/auth/kubeconfig, kubeadmin-password)
bin/openshift-install coreos print-stream-json | grep -oE 'https://[^"]+live-(kernel|initramfs|rootfs)[^"]*'

2 Hetzner: Server + Rescue

Laptop · PowerShell
hcloud primary-ip create --type ipv4 --name okd-sno-ip --location hel1 --assignee-type server
hcloud server create --name okd-sno --type cpx52 --image ubuntu-24.04 `
  --ssh-key laptop --firewall okd-sno-fw --location hel1 --primary-ipv4 okd-sno-ip
hcloud server enable-rescue okd-sno --ssh-key laptop
hcloud server reset okd-sno            # HARD reset - Soft-Reboot geht nicht in den Rescue
Was die Schalter bedeuten

Vier Aufrufe, und der letzte klingt harmloser als er ist:

Schaltergehoert zubewirkt
hcloud primary-ip create --type ipv4 zuerst Reserviert eine oeffentliche IPv4-Adresse als eigenstaendiges Objekt, unabhaengig vom Server. Normalerweise gehoert die Adresse zum Server und ist mit ihm weg; hier bleibt sie bestehen. Das ist der Grund, warum der Cluster spaeter geloescht und aus einem Snapshot neu aufgebaut werden kann, ohne dass DNS und Zertifikate nachgezogen werden muessen. Kostet rund 0,50 EUR im Monat, auch waehrend kein Server daran haengt.
--assignee-type server primary-ip Legt fest, woran die Adresse spaeter haengen darf. Aktuell gibt es nur diese eine Art – die Angabe ist trotzdem Pflicht, sonst lehnt die API den Aufruf ab.
--location hel1 (zweimal) primary-ip, server Muss an beiden Stellen identisch sein. Eine reservierte Adresse ist an ihren Standort gebunden und laesst sich nicht an einen Server in einem anderen Rechenzentrum haengen – der server create scheitert dann, nachdem die Adresse bereits angelegt (und berechnet) ist.
--primary-ipv4 okd-sno-ip server create Haengt die eben reservierte Adresse an den neuen Server, statt eine beliebige neue zu ziehen. Der Name aus dem ersten Aufruf, keine Zahl.
--firewall okd-sno-fw server create Haengt die Firewall schon bei der Erstellung an, damit der Server nicht erst ein paar Minuten ungeschuetzt im Netz steht. Auffaellig ist, was fehlt: kein --user-data-from-file. Das Ubuntu-Image ist hier nur ein Vehikel, um in den Rescue-Modus zu kommen – das eigentliche Betriebssystem wird gleich darueber installiert.
enable-rescue --ssh-key laptop Rescue Schaltet fuer den naechsten Start ein Notfall-Linux ein, das aus dem Netz gebootet wird und die Platte unangetastet laesst. Genau das braucht der coreos-installer spaeter: er ueberschreibt /dev/sda, was aus einem laufenden System heraus nicht ginge. Ohne --ssh-key vergibt Hetzner ein Einmal-Passwort und zeigt es nur einmal an.
hcloud server reset Rescue Ein harter Reset – das Gegenstueck zum Ausschalten am Netzschalter, ohne dass das laufende System etwas davon mitbekommt. Noetig, weil ein sauberes reboot das bestehende System wieder von der Platte startet und den Rescue-Modus schlicht ueberspringt. Auf einem Server mit Daten waere derselbe Befehl gefaehrlich; hier ist die Platte im naechsten Schritt ohnehin dran. Der verwandte Befehl hcloud server reset-password tut etwas voellig anderes – nicht verwechseln.

3 Rescue: kexec in SCOS-Live

Ignition + scripts/sno-kexec.sh ins Rescue kopieren, dann:

Server · Rescue (bash)
KERNEL_URL=… INITRD_URL=… ROOTFS_URL=… IGN=/root/sno.ign bash /root/sno-kexec.sh

Das Skript installiert kexec-tools, laedt Kernel+initramfs, haengt die Ignition als /config.ign (xz-cpio) an das initramfs (genau das macht coreos-installer pxe customize intern – der Container-Weg scheitert im Rescue an fehlendem BPF) und loest systemctl kexec aus. Ab hier laeuft alles selbst.

4 Warten & kubeconfig

WSL2 · bash
bin/openshift-install --dir cfg wait-for install-complete
cp cfg/auth/kubeconfig /mnt/c/Users/<du>/.kube/okd-sno-hetzner.yaml
cat cfg/auth/kubeadmin-password

+ Absicherung

  • API + SSH nur ueber Tailscaleokd-sno-fw lässt nur noch 80/443 offen (App-Routes). 22 und 6443 sind zu. oc/helm/SSH laufen übers Tailnet (siehe Kasten). Klassisch mit Firewall-Regeln von deiner IP geht auch – dann $TailscaleHost = '' in sno.ps1.
    Ausnahme: der Hetzner-Jenkins (Jenkins → OKD) ist nicht im Tailnet – jenkins.ps1 stanzt daher beim up eine eigene 6443-Regel in okd-sno-fw und raeumt sie beim down wieder weg. Ein lokal betriebener Jenkins braucht das nicht.
  • kubeadmin ist Voll-Admin – nach dem Einrichten eines eigenen Identity-Providers (htpasswd/OIDC) abschalten. Die kubeadmin-kubeconfig nicht weitergeben.
  • SCOS updatet sich ueber den Cluster (oc adm upgrade) – kein dnf auf dem Node.
  • SSH zum Node nur als core mit Key.
Tailscale auf SCOS – getestet

curl -fsSL https://tailscale.com/install.sh | sh scheitert auf dem Node: SCOS ist immutable, kein dnf, keine Paketquellen (Error: There are no enabled repositories). Der CoreOS-Weg – statische Binärdateien + systemd-Unit:

Node (SCOS) · sudo
VER=$(curl -fsSL 'https://pkgs.tailscale.com/stable/?mode=json' | grep -oE '"Version":"[0-9.]+"' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
curl -fsSL -o /tmp/ts.tgz "https://pkgs.tailscale.com/stable/tailscale_${VER}_amd64.tgz"
sudo tar xzf /tmp/ts.tgz -C /tmp
cd /tmp/tailscale_${VER}_amd64
sudo install -m 0755 tailscale tailscaled /usr/local/bin/
sudo sed 's#/usr/sbin/tailscaled#/usr/local/bin/tailscaled#' systemd/tailscaled.service | sudo tee /etc/systemd/system/tailscaled.service >/dev/null
sudo cp systemd/tailscaled.defaults /etc/default/tailscaled
# sudo hat /usr/local/bin nicht im PATH -> ergaenzen (SCOS-minimal):
echo 'Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"' | sudo tee /etc/sudoers.d/10-localpath >/dev/null
sudo systemctl daemon-reload && sudo systemctl enable --now tailscaled
sudo /usr/local/bin/tailscale up --ssh --hostname sno   # druckt Login-URL

Danach:

  • Im Admin-Panel bei okd-snoDisable key expiry (sonst faellt der Node nach 180 Tagen raus).
  • hosts-Eintrag auf dem Laptop (Admin-PowerShell), damit der API-Zert-SAN passt – die kubeconfig bleibt unverändert:
    Add-Content $env:SystemRoot\System32\drivers\etc\hosts "<tailscale-ip>`tapi.sno.<ip>.nip.io"
  • $TailscaleHost in sno.ps1 auf den MagicDNS-Namen setzen (<cluster>.<tailnet>.ts.net) → ssh/down/ status/up laufen dann über Tailscale.
  • okd-sno-fw auf nur 80/443 setzen: hcloud firewall replace-rules okd-sno-fw --rules-file <json mit 80+443>.
  • SELinux-Warnung Tailscale SSH may not work: das --ssh-Feature (Login ohne Key) braucht ein SELinux-Modul, das SCOS nicht hat. Klassisches ssh core@<cluster>.<tailnet>.ts.net (key-basiert) geht trotzdem – und reicht fuers „kein offener Port“-Ziel.

Alternativen (rpm-ostree, Container) + ACLs/MagicDNS/Exit-Node: Tailscale-Anleitung.

Fuer den Dauerbetrieb

Grundausstattung-Checkliste – 2FA aufs Hetzner-Konto, Tailscale, Backup ausser Haus, Uptime-Monitoring, Log-Limits.

+ Grenzen & andere Wege

  • Keine HA (Hochverfügbarkeit). Ein Node = ein Ausfallpunkt. Fuer „echt betreiben“ der mehrknotige OKD-Cluster (Stufe 4).
  • RAM statt CPU (Central Processing Unit) ist der Engpass. OKD selbst belegt ~11 GB (CPU < 1 Kern im Leerlauf). cpx42 (16 GB) reicht nur fuer OKD leer, Vorgabe ist darum cpx52 (24 GB). Mit einem ganzen Projekt-Stack (Beispiel: ~15,6 GB gesamt) ist cpx52 knapp bequem, ccx33/cpx62 entspannt.
  • Nur Kubernetes, ohne OpenShift-Schicht: die k3s-Anleitung – leichter, schneller, ~512 MB (Megabyte) Overhead.
  • Nur oc ueben, kostenlos: die Developer Sandbox (Stufe 0) – gehostetes OpenShift von Red Hat, kein Server.
  • nip.io hatte schon laengere Ausfaelle. Fuer mehr als ein Lab eine echte Wildcard-Domain (*.apps.sno.deine-domain) auf die feste IP zeigen lassen.
OptionArtBemerkung
ROSA (Red Hat OpenShift Service on AWS) / ARO (Azure Red Hat OpenShift)managedOpenShift auf AWS (Amazon Web Services) bzw. Azure, mit Console/OperatorHub. ~$0,17/h Control-Plane + Worker.
OpenShift DedicatedmanagedVon Red Hat gehostet auf AWS/GCP (Google Cloud Platform). Ab ~$1k/Monat.
CRC / OpenShift LocallokalSNO auf dem Laptop (~4 vCPU / 9 GB). Zum Entwickeln, nicht „remote“.
Assisted Installerself-hostWeb-UI erzeugt eine Discovery-ISO. Braucht ISO-Boot – bei Hetzner Cloud nicht moeglich, daher hier der kexec-Weg.