Kubernetes YAML ist die Textform, in der du Kubernetes sagst, was im Cluster laufen soll. Eine solche Datei heißt Manifest: Sie beschreibt ein Objekt wie ein Deployment oder einen Service mit vier Pflichtfeldern (apiVersion, kind, metadata, spec), und Kubernetes arbeitet so lange, bis der Cluster genau diesem Zustand entspricht. Du beschreibst das Ziel, nicht den Weg dorthin.
Genau dieser Punkt ist der Grund, warum viele Einsteiger mit YAML hadern: Die Syntax ist schnell gelernt, das Denken in Zielzuständen dauert länger. Und weil YAML sehr tolerant ist, gibt es ein paar Fallen, die dich Stunden im Debugging kosten können, ohne dass ein einziger Syntaxfehler auftaucht.
Ich betreibe Kubernetes-Cluster in Produktion und habe das Kubernetes-Praxisbuch bei Rheinwerk geschrieben (2024). Dort ist YAML ein eigenes Unterkapitel, weil jedes Objekt im Cluster am Ende eine YAML-Datei ist. Dieser Beitrag ist die Kurzfassung mit den Fehlern, die im Betrieb am häufigsten vorkommen. Alle meine Beiträge zum Thema findest du gesammelt auf der Seite Kubernetes.
Deklarative Konfiguration: du beschreibst das Ziel, nicht den Weg
Wenn du aus der klassischen Programmierung kommst, denkst du imperativ: Schritt eins, Schritt zwei, prüfen, Schritt drei. Bei einem Server heißt das: Container starten, prüfen, ob er läuft, bei Absturz neu starten, bei Last einen zweiten daneben stellen. Du bist bei jedem Schritt selbst dabei.
Kubernetes dreht das um. Du schreibst in ein Manifest: „Diese Anwendung soll mit drei Pods laufen." Das ReplicaSet dahinter vergleicht dann laufend den Ist-Zustand mit deinem Soll-Zustand. Fehlt ein Pod, startet es einen neuen. Ist einer zu viel, beendet es ihn. Dieser Kreislauf heißt Reconciliation Loop, und er ist der Kern der deklarativen Konfiguration: Nicht du prüfst und korrigierst, sondern das System.
Daraus ergeben sich die Eigenschaften, die Kubernetes im Betrieb so angenehm machen. Dasselbe Manifest führt immer zum selben Endzustand, egal wie der Cluster gerade aussieht (Idempotenz). Du kannst eine zweite Umgebung aus denselben Dateien hochziehen (Wiederholbarkeit). Und Abweichungen werden ohne dein Zutun korrigiert (Selbstheilung). Grenzen gibt es trotzdem: Wenn das Image im Manifest nicht existiert oder die Registry nicht erreichbar ist, kann Kubernetes den Zustand nicht herstellen und meldet den Fehler. Es findet Wege, aber es zaubert nicht.
Trotzdem hat kubectl imperative Befehle wie kubectl run oder kubectl scale. Die haben ihren Platz: für schnelle, kleine Änderungen, für einmalige Aktionen, beim Debugging und in Entwicklungsumgebungen. In meinen Produktionsclustern gilt aber die Regel: Was nicht als Manifest in Git liegt, existiert nicht. Wenn ich im Notfall imperativ eingreife, ziehe ich die Änderung danach in die Datei nach. Sonst wird sie beim nächsten Apply stillschweigend überschrieben.
Aufbau eines Kubernetes Manifests: die vier Pflichtfelder
Jedes Kubernetes Manifest hat dieselbe Grundstruktur, ob Pod, Deployment, Service oder ConfigMap. Vier Felder auf oberster Ebene sind Pflicht:
apiVersion: Welche API-Gruppe und Version das Objekt definiert. Für ein Deployment ist das apps/v1, für Pod, Service und ConfigMap schlicht v1.
kind: Der Objekttyp, zum Beispiel Deployment.
metadata: Name, Namespace, Labels und Annotations. Der Name muss innerhalb des Namespaces eindeutig sein.
spec: Der gewünschte Zustand. Hier steht alles, was das Objekt ausmacht, und der Inhalt unterscheidet sich je nach kind komplett.
Ein fünftes Feld, status, schreibst nie du, sondern Kubernetes. Dort hält der Cluster den aktuellen Zustand fest, den er mit deiner spec abgleicht. Wenn du mit kubectl get deployment shop-web -o yaml ein Objekt aus dem Cluster liest, siehst du dieses Feld gefüllt. In deine Datei gehört es nicht.
Welche Felder eine spec erlaubt, steht in der API-Referenz auf kubernetes.io. Schneller geht es direkt im Terminal: kubectl explain deployment.spec.template.spec.containers zeigt dir alle Felder des Container-Objekts mit Beschreibung. Diese Punktschreibweise nutzt auch die Dokumentation, um auf ein einzelnes Feld samt Hierarchie zu verweisen, etwa spec.containers[].resources.limits.cpu. Der Punkt trennt Ebenen, die eckigen Klammern zeigen eine Liste an.
Kubernetes YAML Beispiel: ein Deployment Zeile für Zeile
Das Manifest, das ich am häufigsten schreibe, ist ein Deployment. Hier ein vollständiges, lauffähiges Beispiel:
apiVersion: apps/v1
kind: Deployment
metadata:
name: shop-web
namespace: shop
labels:
app: shop-web
spec:
replicas: 3
selector:
matchLabels:
app: shop-web
template:
metadata:
labels:
app: shop-web
spec:
containers:
- name: web
image: nginx:stable
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "64Mi"
limits:
cpu: "500m"
memory: "128Mi"
Lies es von oben nach unten. metadata benennt das Deployment und legt es in den Namespace shop. In spec steht der Zielzustand: drei Replikate. Der selector sagt dem Deployment, welche Pods zu ihm gehören, nämlich alle mit dem Label app: shop-web. Und template ist die Bauvorlage für genau diese Pods: eigenes metadata mit demselben Label, eigene spec mit der Container-Liste. Das Label im Template muss zum Selector passen, sonst lehnt Kubernetes das Manifest ab.
Auffällig ist die Verschachtelung: Ein Deployment enthält eine Pod-Vorlage, die wiederum eine spec hat. Das verwirrt am Anfang, ist aber folgerichtig. Das Deployment beschreibt, wie viele Pods laufen sollen und wie sie ausgetauscht werden. Die Pod-Vorlage beschreibt, was in jedem Pod läuft. Wie das Deployment daraus Rollouts macht, steht in Kubernetes Deployment: Rollout erklärt.
Mit kubectl apply -f deployment.yaml schickst du die Datei an den Cluster. Änderst du später replicas auf fünf und wendest die Datei erneut an, startet Kubernetes zwei zusätzliche Pods. Mehr musst du nicht tun.
YAML-Syntax: Key-Value-Paare, Listen und Einrückung
Jede YAML-Datei lässt sich auf drei Bausteine zurückführen. Key-Value-Paare sind die Grundform: replicas: 3. Listen beginnen mit einem Bindestrich am Zeilenanfang: containers ist eine Liste, jeder Container ein Eintrag. Und verschachtelte Strukturen entstehen durch Einrückung: Alles, was unter metadata eingerückt ist, gehört zu metadata.
Die Einrückung ist damit keine Kosmetik, sondern die Struktur selbst. Zwei Leerzeichen je Ebene sind in Kubernetes-Manifesten üblich, wichtig ist nur, dass du innerhalb einer Datei konsistent bleibst. Tabs sind verboten, YAML akzeptiert ausschließlich Leerzeichen. Ein typisches Fehlerbild: Ein Feld landet durch eine falsche Einrückung im falschen Objekt. Das ist kein Syntaxfehler, der Editor meldet nichts, und du suchst lange, warum das Ressourcen-Limit nicht greift.
Drei Kleinigkeiten, die den Alltag einfacher machen: Kommentare beginnen mit # und sind der größte Vorteil von YAML gegenüber JSON. Mehrere Objekte kannst du in einer Datei mit --- trennen, das nutze ich bei kleinen Anwendungen, während ich bei größeren je Objekt eine Datei anlege. Und die Dateiendung ist .yaml, auch wenn .yml überall funktioniert. Kubernetes-Keys sind in camelCase geschrieben (containerPort, matchLabels), daran halte ich mich auch in eigenen Werten.
Typische YAML-Fallen: Norwegen-Problem, Ports und Versionsnummern
Die größte Schwäche von YAML ist seine Toleranz. Du darfst Strings ohne Anführungszeichen schreiben, und der Parser rät den Typ. Meistens rät er richtig. Wenn nicht, bekommst du keinen Fehler, sondern einen anderen Wert.
Das bekannteste Beispiel ist das Norwegen-Problem: Der Ländercode NO wird von Parsern nach der alten YAML-Spezifikation als Boolean false gelesen. Die Spezifikation 1.2 aus dem Jahr 2009 hat die Boolean-Werte auf true und false eingegrenzt, aber viele Bibliotheken parsen weiter nach dem alten Verhalten. Kubernetes nutzt die go-yaml-Bibliothek, und dort wird das Thema seit Jahren in einem offenen Issue diskutiert.
| Wert ohne Anführungszeichen |
So liest ihn ein Parser nach alter Spezifikation |
Sicher geschrieben |
NO |
Boolean false |
"NO" |
yes, on, y |
Boolean true |
"yes" |
22:22 |
Zeitangabe oder Zahl statt Port-Mapping |
"22:22" |
2.1 |
Gleitkommazahl 2.1 |
"2.1" |
1.1.0 |
String (drei Zahlen sind keine Gleitkommazahl) |
bleibt sicher |
In Kubernetes triffst du das an einer Stelle besonders oft: Umgebungsvariablen. Der Wert einer env-Variable muss ein String sein. Schreibst du value: true ohne Anführungszeichen, lehnt der Cluster das Manifest ab, weil er einen Boolean bekommt, wo ein String erwartet wird. Mit value: "true" läuft es. Dasselbe gilt für Versionsnummern mit zwei Stellen und für alles, was wie eine Uhrzeit aussieht.
Meine Regel daraus: Strings, die auch nur entfernt wie etwas anderes aussehen könnten, kommen in Anführungszeichen. Im Deployment oben siehst du das bei "500m" und "128Mi". Kritisch wurde es bei mir dadurch nie, aber die Debugging-Zeit hätte ich mir gern gespart.
Kubernetes YAML prüfen: Validator, Linter und Dry-Run
Bevor ein Manifest in den Cluster geht, prüfe ich es in drei Stufen. Die erste ist der Editor: Ein YAML-Plugin mit Kubernetes-Schema markiert falsche Einrückungen und unbekannte Felder schon beim Tippen. Ein Linter wie yamllint macht dasselbe in der Pipeline.
Die zweite Stufe ist kubectl selbst:
kubectl apply -f deployment.yaml --dry-run=client
kubectl apply -f deployment.yaml --dry-run=server
kubectl diff -f deployment.yaml
--dry-run=client prüft nur die Struktur lokal, ohne den Cluster zu fragen. --dry-run=server schickt das Manifest an den API-Server, der es vollständig validiert, inklusive Admission-Webhooks und Policies, aber nichts speichert. Das ist der ehrlichste Kubernetes YAML Validator, den du hast, weil er genau die Prüfung fährt, die auch beim echten Apply läuft. kubectl diff zeigt dir dazu, was sich im Cluster tatsächlich ändern würde. Die vollständige Referenz zu diesen Befehlen steht in der kubectl-Dokumentation.
Die dritte Stufe ist die CI-Pipeline ohne Clusterzugriff. Dort greift ein Schema-Werkzeug wie kubeconform, das Manifeste gegen die Kubernetes-API-Schemas prüft, ohne dass ein Cluster erreichbar sein muss. Was in Produktion bei mir zusätzlich greift, sind Policies über Gatekeeper, die zum Beispiel Manifeste ohne Ressourcen-Limits ablehnen. Aber das ist ein eigenes Thema.
Anker, Aliase und die Frage nach Kustomize oder Helm
YAML kann Wiederholungen vermeiden: Ein Anker &name markiert einen Wert oder ein ganzes Objekt, ein Alias *name setzt ihn an anderer Stelle wieder ein, und <<: *name mischt die Felder eines Objekts in ein anderes. Wichtig ist dabei, wo diese Abkürzungen enden: Anker, Aliase und Merge-Keys löst der YAML-Parser auf, bevor irgendetwas an Kubernetes geht. Der API-Server bekommt anschließend eine fertige JSON-Struktur ohne jede Spur davon. Das heißt auch, dass kubectl get -o yaml deine Anker nie zurückgibt und dass ein Merge-Key keine Kubernetes-Funktion ist, auf die du dich im Cluster berufen kannst. In Kubernetes-Manifesten brauche ich das deshalb selten. Wo es mir täglich hilft, ist die Pipeline-Definition in GitLab CI, in der sich Skriptblöcke über viele Jobs wiederholen.
Für Kubernetes stellt sich die Wiederholungsfrage anders: Du hast dasselbe Deployment für Entwicklung, Test und Produktion, mit anderen Replikaten, anderen Images, anderen Ressourcen. Anker helfen da nicht, weil die Umgebungen in verschiedenen Dateien liegen. Dafür gibt es Kustomize, das Basis-Manifeste mit Patches überlagert, und Helm, das aus Templates und Values fertige Manifeste erzeugt. Welches Werkzeug wann sinnvoll ist, vergleiche ich in Kustomize vs Helm: was wann?.
Und sobald deine Manifeste in Git liegen, ist der nächste Schritt naheliegend: Ein Werkzeug wie ArgoCD liest sie aus dem Repository und hält den Cluster darauf synchron. Dann ist Git der Soll-Zustand und der Reconciliation Loop reicht bis in dein Repository.
Häufige Fragen
Was ist der Unterschied zwischen Kubernetes Manifest und YAML?
YAML ist das Dateiformat, ein Manifest ist der Inhalt: die Beschreibung eines Kubernetes-Objekts mit apiVersion, kind, metadata und spec. Jedes Manifest ist YAML (oder JSON), aber nicht jede YAML-Datei ist ein Manifest. Eine GitLab-Pipeline oder eine Docker-Compose-Datei ist ebenfalls YAML, nur mit einer völlig anderen Struktur.
Muss ich YAML nehmen oder geht auch JSON?
Der Kubernetes-API-Server spricht intern JSON, und kubectl wandelt deine YAML-Datei vor dem Senden um. Du kannst Manifeste also auch als JSON schreiben und mit kubectl apply -f anwenden. In der Praxis macht das fast niemand, weil YAML Kommentare erlaubt, weniger Klammern braucht und für Menschen deutlich besser lesbar ist.
Wie finde ich heraus, welche Felder ein Objekt hat?
Mit kubectl explain, gefolgt vom Pfad in Punktschreibweise, zum Beispiel kubectl explain pod.spec.containers.resources. Das Kommando liest die Schemas direkt aus deinem Cluster und zeigt zu jedem Feld Typ und Beschreibung. Für den Überblick über alle Objekte ist die API-Referenz auf kubernetes.io die richtige Adresse.
Welcher Kubernetes YAML Validator ist sinnvoll?
Für die schnelle Prüfung kubectl apply --dry-run=server, weil der API-Server damit dieselbe Validierung fährt wie beim echten Apply. Für die CI-Pipeline ohne Clusterzugang ein Schema-Prüfer wie kubeconform. Und im Editor ein YAML-Plugin mit Kubernetes-Schema, das falsche Einrückungen sofort anzeigt. Ein einzelnes Werkzeug fängt nicht alles, die drei Stufen zusammen fangen den größten Teil ab.
Heißt die Datei .yaml oder .yml?
Beides funktioniert, kubectl ist es egal. Die YAML-Dokumentation empfiehlt .yaml, und daran halte ich mich in allen Projekten. Wichtig ist nur, dass du dich innerhalb eines Repositories für eine Endung entscheidest, damit Suchmuster in Pipelines und Editoren zuverlässig alle Dateien finden.
Wie du weitermachst
Wenn du die vier Pflichtfelder und die Fallen kennst, ist der nächste sinnvolle Schritt, das wichtigste Manifest wirklich zu verstehen: Kubernetes Deployment: Rollout erklärt zeigt, wie aus der Pod-Vorlage Rollouts und Rollbacks werden. Sobald du mehrere Umgebungen pflegst, lohnt der Blick auf Kustomize vs Helm: was wann?, und wenn deine Manifeste in Git liegen, ist GitOps mit ArgoCD: Deployments aus Git der Weg, sie automatisch in den Cluster zu bringen.
Ausführlich mit allen Beispielen, von den YAML-Grundlagen über Anker bis zu Versionsverwaltung und Kustomize, steht das in Kapitel 4 meines Kubernetes-Praxisbuchs bei Rheinwerk.