← Ü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 löschen = 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 über HTTPS (Hypertext Transfer Protocol Secure) mit dem API-Server. Die Installation selbst läuft in WSL2openshift-install gibt es nur für 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 unveränderliches 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. Lösung: vom Rescue-System per kexec in die SCOS-Live-Umgebung springen (Ignition als /config.ign ins initramfs eingebettet). Die Live-Umgebung führt 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).

Löst MicroShift ab

Die frühere MicroShift-Fassung ist raus: ihr Installations-Repo (@redhat-et/microshift-COPR) liefert für CentOS Stream 9 nur noch einen Build von 2022 mit unauflösbaren Abhängigkeiten. MicroShift wird heute praktisch nur noch auf RHEL (Red Hat Enterprise Linux) 9.4+ mit Subscription unterstützt – 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).

zusätzlich WSL2

openshift-install läuft nur unter Linux. Einmalig eine WSL2-Distribution installieren (Neustart nötig):

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 über dieselbe öffentliche IP (Internet Protocol) wie der Laptop – die Firewall-Regel für 6443 passt also automatisch.
  • Server-Größe: das offizielle SNO-Minimum ist 8 vCPU / 16 GB (cpx42) – das reicht für OKD leer (OKD belegt selbst schon ~11 GB RAM, CPU ist nie das Thema). sno.ps1 nimmt darum standardmäßig cpx52 (12 vCPU / 24 GB), damit auch eigene Workloads Platz haben. ccx33 (8 dediziert / 32 GB) läuft 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 läuft 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 hängen
  • Server okd-sno (cpx52, Ubuntu als Rescue-Träger) 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) anhängen, 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 hängt

Der häufigste Punkt ist zu wenig RAM (auf cpx42) während der Operator-Konvergenz. oc get clusterversion / oc get co zeigen den Stand. Oft läuft es mit ein paar Minuten mehr doch durch. Sonst .\scripts\sno.ps1 destroy und mit dem größeren Vorgabe-Typ (cpx52) oder -Type ccx33 neu.

Bricht install direkt nach „kexec … auslösen“ ab, läuft der Server trotzdem schon los. Prüfen: 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)

Während wait-for läuft, 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 prüfen

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 öffnet 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 bestätigen.

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

05 Erstes Projekt, App & Route

Normaler OpenShift-Workflow – wie im großen 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 löschen. 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), während er läuft~0,138~3,30~100
Server CPX42 (16 GB, nur OKD leer), während er läuft~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 müsste man neu installieren.

Ein Klick / nächtliches Auto-Down

.\scripts\create-shortcuts.ps1 -For sno legt Desktop-Icons OKD-SNO-up/-down/-status an. .\scripts\install-autodown.ps1 -For sno löscht den Node jede Nacht um 03:00 (Windows-Aufgabenplanung). Der wichtigste Kostenschutz, weil Hetzner kein Ausgabenlimit hat. Voller Kill-Switch-Überblick: 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:

Schaltergehört zubewirkt
hcloud primary-ip create --type ipv4 zuerst Reserviert eine öffentliche IPv4-Adresse als eigenständiges Objekt, unabhängig vom Server. Normalerweise gehört die Adresse zum Server und ist mit ihm weg; hier bleibt sie bestehen. Das ist der Grund, warum der Cluster später gelöscht und aus einem Snapshot neu aufgebaut werden kann, ohne dass DNS und Zertifikate nachgezogen werden müssen. Kostet rund 0,50 EUR im Monat, auch während kein Server daran hängt.
--assignee-type server primary-ip Legt fest, woran die Adresse später hängen 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 hängen – der server create scheitert dann, nachdem die Adresse bereits angelegt (und berechnet) ist.
--primary-ipv4 okd-sno-ip server create Hängt 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 Hängt die Firewall schon bei der Erstellung an, damit der Server nicht erst ein paar Minuten ungeschützt im Netz steht. Auffällig 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 darüber installiert.
enable-rescue --ssh-key laptop Rescue Schaltet für den nächsten Start ein Notfall-Linux ein, das aus dem Netz gebootet wird und die Platte unangetastet laesst. Genau das braucht der coreos-installer später: er überschreibt /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 Gegenstück zum Ausschalten am Netzschalter, ohne dass das laufende System etwas davon mitbekommt. Nötig, weil ein sauberes reboot das bestehende System wieder von der Platte startet und den Rescue-Modus schlicht überspringt. Auf einem Server mit Daten wäre derselbe Befehl gefährlich; hier ist die Platte im nächsten Schritt ohnehin dran. Der verwandte Befehl hcloud server reset-password tut etwas völlig 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, lädt Kernel+initramfs, hängt 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 löst systemctl kexec aus. Ab hier läuft 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 über 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 räumt 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 über 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 fällt 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 fürs „kein offener Port“-Ziel.

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

Für den Dauerbetrieb

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

+ Grenzen & andere Wege

  • Keine HA (Hochverfügbarkeit). Ein Node = ein Ausfallpunkt. Für „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 für 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 üben, kostenlos: die Developer Sandbox (Stufe 0) – gehostetes OpenShift von Red Hat, kein Server.
  • nip.io hatte schon längere Ausfaelle. Für 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 möglich, daher hier der kexec-Weg.