Blog · 15. April 2025 · Aktualisiert am 7. September 2026 · 12 Min. Lesezeit

Kubernetes Labels und Annotations erklärt

Gestapelte schwarze Ordner mit farbigen Klebeetiketten an den Rücken
Foto: Jakub Zerdzicki / Pexels

Kubernetes Labels sind Schlüssel-Wert-Paare, die du an jedes Objekt im Cluster hängst, damit Kubernetes und du diese Objekte finden, gruppieren und verknüpfen können. Ein Service findet seine Pods über Labels, ein Deployment erkennt daran seine ReplicaSets, und du filterst damit in kubectl. Annotations sind ebenfalls Schlüssel-Wert-Paare, aber für Informationen, nach denen niemand filtert: Build-Daten, Ansprechpartner oder Einstellungen für Werkzeuge wie den Ingress-Controller.

Der Unterschied klingt klein, entscheidet aber darüber, ob dein Cluster nach einem Jahr noch übersichtlich ist. Falsch gesetzte Labels führen zu Services, die ins Leere zeigen, und zu Deployments, die fremde Pods einsammeln. Fehlende Annotations führen dazu, dass niemand mehr weiß, welcher Build gerade läuft und wer dafür zuständig ist.

Ich betreibe Kubernetes-Cluster in Produktion und habe das Kubernetes-Praxisbuch bei Rheinwerk geschrieben (2024). Labels und Annotations haben dort im dritten Kapitel einen eigenen Abschnitt. Hier steht die Kurzfassung mit den Regeln, die sich bei mir im Betrieb bewährt haben. Alle Beiträge zum Thema findest du gesammelt auf meiner Seite Kubernetes.

Kubernetes Labels vs. Annotations: der Unterschied

Stell dir einen Aktenschrank vor. Ein Label ist das Etikett am Ordnerrücken: Es hilft dir, den richtigen Ordner oder alle Ordner eines Kunden auf einen Blick zu finden. Eine Annotation ist das Inhaltsverzeichnis im Ordner: Beim Suchen hilft es dir nicht, aber es sagt dir genau, was drinsteckt und wer es abgelegt hat.

Technisch stehen beide unter metadata, beide sind Zeichenketten, und beide kannst du jederzeit ändern. Der Unterschied liegt darin, wer sie liest. Labels wertet Kubernetes selbst aus: Ein Service, ein Deployment, ein ReplicaSet oder eine NetworkPolicy wählt darüber Pods aus, der Scheduler wählt darüber Nodes. Annotations ignoriert der Kubernetes-Kern. Sie sind für Menschen gedacht und für Werkzeuge, die auf dem Cluster aufsetzen, etwa ein Ingress-Controller, cert-manager oder ArgoCD.

Labels Annotations
Zweck identifizieren, gruppieren, auswählen beschreiben, konfigurieren, dokumentieren
Wer liest sie Kubernetes-Kern über Selektoren, kubectl, du Menschen, Controller und externe Werkzeuge
Filterbar mit Selektoren ja nein
Wert höchstens 63 Zeichen, eingeschränkter Zeichensatz beliebiger Text, auch JSON oder mehrzeilig
Größe klein und kurz alle Annotations eines Objekts zusammen bis 256 KiB
Typische Beispiele app.kubernetes.io/name, environment kubectl.kubernetes.io/last-applied-configuration, prometheus.io/scrape

Die 256 KiB sind dabei keine Grenze je Annotation, sondern die Summe aller Annotations eines Objekts, Schlüssel und Werte zusammengerechnet, so steht es in der Kubernetes-Dokumentation zu Annotations. Werte dürfen anders als bei Labels beliebige Zeichen enthalten, auch JSON oder YAML.

Meine Faustregel: Willst du jemals nach dem Wert filtern oder darüber Objekte auswählen, ist es ein Label. Ist es reine Information, ist es eine Annotation. Wer diese eine Frage bei jedem Schlüssel stellt, macht den Rest fast automatisch richtig.

Labels setzen: Syntax, Präfixe und Grenzen

Ein Label besteht aus Schlüssel und Wert. Der Schlüssel hat einen Namen und optional ein Präfix, getrennt durch einen Schrägstrich. Der Name darf bis zu 63 Zeichen lang sein, aus Buchstaben, Ziffern, Bindestrichen, Unterstrichen und Punkten bestehen und muss mit einem Buchstaben oder einer Ziffer beginnen und enden. Das Präfix ist ein DNS-Subdomain-Name wie example.com mit bis zu 253 Zeichen. Für den Wert gelten dieselben Zeichenregeln und dieselben 63 Zeichen, er darf aber auch leer sein.

Die Präfixe kubernetes.io/ und k8s.io/ sind für Kubernetes-Komponenten reserviert. Schlüssel ohne Präfix gehören dir und deinem Team. Sobald du Werkzeuge baust, die mehrere Teams oder Cluster betreffen, nimmst du deine eigene Domain als Präfix, damit sich niemand mit einem gleichnamigen Schlüssel in die Quere kommt. So sieht ein Pod mit drei Labels aus:

apiVersion: v1
kind: Pod
metadata:
  name: shop-web
  labels:
    app.kubernetes.io/name: shop-web
    app.kubernetes.io/part-of: shop
    environment: production
spec:
  containers:
    - name: web
      image: nginx:stable
      ports:
        - containerPort: 80

Labels kannst du auch nachträglich an laufende Objekte hängen, ändern oder entfernen, ohne dass der Pod neu startet. Ein Minus hinter dem Schlüssel entfernt das Label, --overwrite brauchst du, sobald der Schlüssel schon existiert:

kubectl label pod shop-web tier=frontend
kubectl label pod shop-web tier=backend --overwrite
kubectl label pod shop-web tier-
kubectl get pods --show-labels
kubectl get pods -L environment,tier

Die letzten beiden Befehle zeigen dir, was gesetzt ist: --show-labels hängt alle Labels als Spalte an, -L blendet nur die genannten Schlüssel als eigene Spalten ein. Was der Pod selbst ist und wie er aufgebaut ist, erkläre ich in Kubernetes Pod: was ist ein Pod?.

Label Selector: wie kubectl, Service und Deployment ihre Pods finden

Ein Label allein tut nichts. Interessant wird es durch den Label Selector, mit dem du oder ein Kubernetes-Objekt Ressourcen anhand ihrer Labels auswählt. Es gibt zwei Schreibweisen. Gleichheits-Selektoren prüfen auf einen genauen Wert mit = oder !=. Mengen-Selektoren prüfen, ob ein Wert in einer Liste liegt oder ob ein Schlüssel überhaupt gesetzt ist:

kubectl get pods -l environment=production
kubectl get pods -l 'environment in (production,staging)'
kubectl get pods -l 'environment notin (test)'
kubectl get pods -l environment
kubectl get pods -l '!environment'
kubectl get pods -l app.kubernetes.io/name=shop-web,environment=production

Zwei Details stolpern viele: notin liefert auch alle Pods, die das Label gar nicht haben. Und mehrere Bedingungen, durch Komma getrennt, sind immer eine UND-Verknüpfung. Ein ODER über verschiedene Schlüssel gibt es in einem Selektor nicht. Ergänzend gibt es Field Selectors wie --field-selector status.phase=Running, die nicht auf Labels, sondern auf Felder des Objekts schauen; sie kennen nur = und != und nicht jedes Feld.

Dieselben Selektoren stecken in den Objekten selbst. Ein Service leitet Traffic an alle Pods, deren Labels zu seinem Selektor passen. Passt kein Pod, hat der Service keine Endpoints, und Anfragen laufen ins Leere. Das ist bei mir der erste Blick, wenn ein Service nicht antwortet: kubectl describe service shop-web und die Zeile Endpoints lesen.

apiVersion: v1
kind: Service
metadata:
  name: shop-web
spec:
  selector:
    app.kubernetes.io/name: shop-web
    environment: production
  ports:
    - port: 80
      targetPort: 80

Beim Deployment gilt eine strengere Regel: spec.selector.matchLabels muss zu den Labels in spec.template.metadata.labels passen, sonst lehnt der API-Server das Manifest ab. Der Selektor eines Deployments ist nach dem Anlegen unveränderlich. Deshalb gehören nur stabile Schlüssel hinein, etwa Name und Instanz, niemals Version oder Build. Wer die Version in den Selektor schreibt, kann sie nie wieder ändern, ohne das Deployment zu löschen. Für komplexere Regeln erlaubt das Deployment neben matchLabels auch matchExpressions mit den Operatoren In, NotIn, Exists und DoesNotExist. Wie das Deployment darüber seine ReplicaSets und Pods verwaltet, steht in Kubernetes Deployment: Rollout erklärt.

Die empfohlenen app.kubernetes.io-Labels

Kubernetes schreibt dir keine Labels vor, empfiehlt aber einen Satz gemeinsamer Schlüssel. Werkzeuge wie Helm, ArgoCD, Dashboards und viele Operatoren erkennen sie und gruppieren deine Objekte danach. Helm setzt app.kubernetes.io/managed-by selbst, ArgoCD nutzt standardmäßig app.kubernetes.io/instance, um zu verfolgen, welche Objekte zu welcher Anwendung gehören.

Schlüssel Bedeutung Beispiel
app.kubernetes.io/name Name der Anwendung shop-web
app.kubernetes.io/instance eindeutiger Name dieser Installation shop-web-prod
app.kubernetes.io/version Version der Anwendung, Format frei 2.4.1
app.kubernetes.io/component Rolle innerhalb der Architektur frontend
app.kubernetes.io/part-of übergeordnete Anwendung shop
app.kubernetes.io/managed-by Werkzeug, das das Objekt verwaltet helm

Die vollständige Liste steht in der Kubernetes-Dokumentation zu den empfohlenen Labels. In meinen Helm-Charts setze ich dieses Set an jedem Objekt, vom Deployment über den Service bis zum Ingress. Der Gewinn zeigt sich im Alltag: kubectl get all -l app.kubernetes.io/part-of=shop zeigt mir die komplette Anwendung mit einem Befehl, und ein neuer Kollege versteht die Zusammenhänge im Cluster, ohne mich zu fragen.

Annotations in der Praxis: was Kubernetes und Werkzeuge dort ablegen

Annotations begegnen dir, bevor du selbst eine schreibst. Nach jedem kubectl apply hängt Kubernetes die Annotation kubectl.kubernetes.io/last-applied-configuration an das Objekt, mit dem kompletten zuletzt ausgerollten Manifest als JSON. Daraus berechnet kubectl beim nächsten Apply, welche Felder du entfernt hast. Deployments zählen in deployment.kubernetes.io/revision ihre Revisionen hoch, damit ein Rollback weiß, wohin er zurück soll.

Werkzeuge außerhalb des Kerns nutzen Annotations als Konfigurationsschnittstelle. Ein Ingress-Controller liest zum Beispiel nginx.ingress.kubernetes.io/rewrite-target, cert-manager reagiert auf cert-manager.io/cluster-issuer und besorgt das Zertifikat, ArgoCD steuert die Reihenfolge des Ausrollens über argocd.argoproj.io/sync-wave. Viele Prometheus-Konfigurationen erkennen an prometheus.io/scrape und prometheus.io/port, welche Pods Metriken anbieten. Das Muster ist immer dasselbe: Das Kubernetes-Objekt bleibt Standard, das Werkzeug holt sich seine Einstellungen aus den Annotations.

Für eigene Annotations gibt es zwei typische Fälle: Zuständigkeit und Herkunft. Ein Kunde von mir hinterlegt dort den Schutzbedarf der Anwendung, ich hänge in Pipelines gern die Build-Informationen als JSON an. YAML erlaubt dafür mehrzeilige Werte:

apiVersion: v1
kind: Pod
metadata:
  name: shop-web
  annotations:
    example.com/owner: "team-shop"
    prometheus.io/scrape: "true"
    example.com/build: |
      {"repo": "shop-web", "commit": "a1b2c3d", "built": "2025-03-28T09:14:00Z"}
spec:
  containers:
    - name: web
      image: nginx:stable

Achte auf die Anführungszeichen bei "true". Annotations sind Zeichenketten, und YAML würde ein nacktes true als Wahrheitswert lesen, was der API-Server ablehnt. Auf der Kommandozeile setzt du Annotations mit kubectl annotate, nach denselben Regeln wie bei Labels:

kubectl annotate deployment shop-web example.com/owner="team-shop"
kubectl annotate deployment shop-web example.com/owner="team-platform" --overwrite
kubectl annotate deployment shop-web example.com/owner-

Ein Detail, das dir Rollouts erklärt: Eine Annotation an den metadata eines Deployments ändert nichts am laufenden Workload. Eine Annotation im Pod-Template unter spec.template.metadata dagegen ändert das Template und löst ein Rollout aus. Genau so arbeitet kubectl rollout restart: Es schreibt eine Annotation mit dem Zeitstempel ins Template, und Kubernetes tauscht die Pods aus.

Labels auf Nodes: Scheduling steuern

Nicht nur Pods tragen Labels. Jede Node bringt von Haus aus Labels wie kubernetes.io/hostname, kubernetes.io/os und kubernetes.io/arch mit, Control-Plane-Nodes zusätzlich eine Rollen-Markierung, und in der Cloud oder in verteilten Clustern kommt topology.kubernetes.io/zone dazu. Der Scheduler nutzt diese Labels, wenn du ihm sagst, wo ein Pod laufen soll.

Der einfachste Weg ist der nodeSelector. Eine Anwendung, die eine GPU braucht, darf nur auf Nodes starten, die entsprechend markiert sind:

apiVersion: v1
kind: Pod
metadata:
  name: modell-training
spec:
  nodeSelector:
    accelerator: nvidia-gpu
  containers:
    - name: training
      image: registry.example.com/training:stable

Gibt es keine Node mit diesem Label, bleibt der Pod in Pending, bis eine auftaucht. Für weichere Regeln gibt es Node Affinity, die ein Ziel bevorzugt statt erzwingt, und Pod Anti-Affinity, die Replikate derselben Anwendung auf verschiedene Nodes oder Zonen verteilt. Beide arbeiten wieder mit Label Selectors. In Clustern, die ich über mehrere Rechenzentren verteile, ist das Zonen-Label die Grundlage dafür, dass der Ausfall eines Standorts nicht alle Replikate trifft. Die Gegenrichtung, also eine Node, die Pods abweist, regeln Taints und Tolerations; das ist ein eigenes Thema.

Kubernetes Labels Best Practices aus dem Betrieb

Erstens: Das empfohlene Set aus app.kubernetes.io überall setzen, plus ein Label für die Umgebung. In meinen Charts steht das als Template, damit kein Objekt ohne Labels entsteht. Ein einzelnes Objekt ohne Labels ist harmlos, fünfzig davon sind ein Cluster, in dem niemand mehr etwas findet.

Zweitens: Selektoren klein halten und nur stabile Schlüssel hineinnehmen. Für Service und Deployment reichen app.kubernetes.io/name und app.kubernetes.io/instance. Alles, was sich mit jedem Build ändert, gehört als Information in ein Label wie version, aber nie in den Selektor, und die ausführliche Fassung mit Commit und Zeitstempel in eine Annotation.

Drittens: Eigene Schlüssel mit eigener Domain als Präfix. Ein nacktes team ist in einem kleinen Cluster in Ordnung, in einem geteilten Cluster kollidiert es früher oder später mit dem team-Label eines anderen Teams oder eines Operators.

Viertens: Labels sind kein Sicherheitsmerkmal. Eine NetworkPolicy wählt Pods über Labels aus, aber wer Schreibrechte auf den Namespace hat, kann einem Pod jedes Label geben und ihn damit in eine Policy hinein- oder herausbewegen. Die Vertrauensgrenze zieht RBAC, nicht das Label. Bei mir gilt: Wer Labels in Produktion ändern darf, darf auch alles andere dort ändern, und genau deshalb dürfen es sehr wenige.

Fünftens: Vorsicht beim Löschen über Selektoren. kubectl delete all -l app.kubernetes.io/part-of=shop ist ein wunderbarer Aufräumbefehl und ein wunderbarer Weg, eine ganze Anwendung zu entfernen, die jemand anderes mit demselben Label versehen hat. Vorher immer kubectl get all -l ... mit demselben Selektor ansehen. Die vollständigen Regeln zu Syntax und Selektoren stehen in der Kubernetes-Dokumentation zu Labels und Selektoren, die von Kubernetes selbst genutzten Schlüssel in der Referenz der Well-Known Labels, Annotations und Taints.

Häufige Fragen

Was ist der Unterschied zwischen Labels und Annotations in Kubernetes?

Labels sind kurze Schlüssel-Wert-Paare, über die Kubernetes Objekte auswählt: Services, Deployments und NetworkPolicies finden darüber ihre Pods, und du filterst damit in kubectl. Annotations sind Schlüssel-Wert-Paare für Zusatzinformationen, nach denen sich nicht filtern lässt, dafür mit beliebig langen Werten, die Menschen und Werkzeuge wie Ingress-Controller oder cert-manager lesen.

Kann ich Labels an laufenden Pods ändern?

Ja, mit kubectl label, und der Pod läuft dabei ungestört weiter. Interessant wird das beim Fehlersuchen: Entfernst du an einem Pod das Label, über das sein ReplicaSet und sein Service ihn finden, bekommt der Pod keinen Traffic mehr und das ReplicaSet startet einen Ersatz. Der alte Pod bleibt als Waise am Leben, und du kannst ihn in Ruhe untersuchen, ohne dass Nutzer etwas merken.

Sind Labels ein Sicherheitsmerkmal?

Nein. Labels steuern, welche Pods eine NetworkPolicy oder ein Service auswählt, aber jeder mit Schreibrecht auf das Objekt kann sie ändern. Sicherheit entsteht durch RBAC, das festlegt, wer Objekte und damit Labels ändern darf, und durch Admission-Regeln, die bestimmte Labels erzwingen oder verbieten.

Wie viele Labels sollte ein Objekt haben?

Kubernetes setzt keine feste Obergrenze für die Anzahl, nur die Größe des ganzen Objekts begrenzt sie. Bei mir tragen Objekte das empfohlene Set aus app.kubernetes.io plus Umgebung und Team, mehr selten. Jedes weitere Label sollte eine Frage beantworten, die du wirklich stellst, etwa „Welche Objekte gehören zu Kunde X?" oder „Was läuft in Staging?".

Was passiert, wenn der Selektor eines Service auf keinen Pod passt?

Der Service existiert weiter und hat eine IP-Adresse, aber keine Endpoints. Anfragen an ihn schlagen fehl, meist mit Connection Refused oder einem Timeout. Prüfe mit kubectl describe service <name> die Zeile Endpoints und vergleiche den Selektor mit kubectl get pods --show-labels. Fast immer ist es ein Tippfehler in einem Schlüssel oder ein Wert, der im Deployment anders heißt als im Service.

Wie du weitermachst

Labels sind das Bindeglied zwischen fast allen Objekten, die du in den nächsten Beiträgen kennenlernst. Wie ein Service über den Selektor Traffic an die richtigen Pods verteilt, steht in Kubernetes Service Types erklärt. Wie ein Deployment über matchLabels seine ReplicaSets und Pods verwaltet und was beim Rollout passiert, steht in Kubernetes Deployment: Rollout erklärt.

Mein Vorschlag für heute: Nimm eine Anwendung aus deinem Cluster, versieh alle ihre Objekte mit dem empfohlenen Label-Set und schau, ob kubectl get all -l app.kubernetes.io/part-of=<name> alles zeigt, was dazugehört. Was fehlt, fehlt auch deinem Dashboard und deinem Nachfolger.

Ausführlich mit allen Beispielen, inklusive Node Affinity, Pod Anti-Affinity und Taints, steht das in Kapitel 3 meines Buchs „Kubernetes: Das Praxisbuch für Entwickler und DevOps-Teams" (Rheinwerk). Alle Informationen zum Buch findest du auf meiner Kubernetes-Seite.

Kevin Welter

Kevin Welter

Entwickler, IT-Architekt, Fachbuchautor (Kubernetes, Cloud-Infrastrukturen) und Speaker. Betreibt sein Business mit einer KI-Belegschaft aus acht KI-Mitarbeitern und zeigt Selbstständigen in seiner Community, wie sie ihren ersten KI-Mitarbeiter einstellen.

Mehr über KI-Mitarbeiter

Kubernetes von den Grundlagen bis zum produktionsreifen Cluster

Zum Buch