| Tested on | Rocky Linux 10.2 (Red Quartz) workstation |
|---|---|
| Package | kubectl 1.36.3 |
| Applies to | Any host with kubectl configured; any Kubernetes cluster |
| Cert prep | CKAD · CKA · CKS |
| Lab environment | Multi-node kubeadm cluster with containerd — install Kubernetes with kubeadm |
| Privilege | Normal user (no sudo required on the workstation) |
| Scope | Annotation syntax, when to use annotations, metadata.annotations in YAML, kubectl annotate (add, overwrite, remove), viewing annotations, and common non-selector metadata patterns. Does not cover label selectors or a full labels vs annotations comparison. |
| Related guides | Kubernetes labels and selectors Kubernetes pods Deployments and rolling updates kubectl commands and YAML examples |
Annotations record descriptive metadata on Kubernetes objects—Git commits, build IDs, owner contacts, documentation URLs—without changing how workloads are selected or grouped. kubectl get -l, Service selectors, and Deployment spec.selector read labels only; annotations sit in metadata.annotations for people, dashboards, and tools that understand specific keys.
This guide walks through annotation syntax, adding keys in a YAML manifest or with kubectl annotate, and reading them back with --list, kubectl describe, and JSONPath. If you are deciding between a label and an annotation, read the labels vs annotations comparison first. Selector grammar and kubectl label live in the labels and selectors guide.
Quick reference: kubectl annotate commands
| Task | Command |
|---|---|
| Add an annotation | kubectl annotate deployment web example.com/owner=platform-team |
| Overwrite an existing value | kubectl annotate deployment web example.com/owner=app-team --overwrite |
| Remove an annotation | kubectl annotate deployment web example.com/owner- |
| List annotations on an object | kubectl annotate deployment web --list |
| View annotations in YAML | kubectl get deployment web -o yaml |
| Print one annotation | kubectl get deployment web -o jsonpath='{.metadata.annotations.example\.com/owner}{"\n"}' |
The examples use a Deployment named web. Update annotations on existing objects with kubectl creates that Deployment in an annotations-demo namespace.
What are Kubernetes annotations?
Annotations live under metadata.annotations on any API object that supports metadata. They store descriptive data that should not drive selection:
- Documentation or runbook URLs
- Git commit, build ID, or release pipeline metadata
- Owner or on-call contact strings
- Structured JSON or YAML for tools that understand a specific key
Unlike labels, annotation values can be long arbitrary strings. The API still enforces limits: keys follow the same optional DNS prefix and name rules as label keys, and the total size of all annotation keys and values on one object must not exceed 256 KiB.
Controllers may read annotations they define. For example, an Ingress controller watches metadata.annotations on Ingress objects for TLS or rewrite settings. That is controller-specific behavior, not a general selector mechanism.
When to use annotations
Reach for annotations when the metadata describes an object but must not participate in selection. If kubectl get -l, a Service spec.selector, or a Deployment spec.selector needs to match the value, use a label on metadata.labels instead — the labels and selectors guide covers that path.
Use annotations for:
- Release and build traceability — Git commit, image digest, CI build ID, or pipeline run URL that changes every deploy
- People and process — owner, on-call rotation, or team contact strings you want visible in
kubectl describe - Documentation — runbook, architecture diagram, or ticket URLs (often too long for label values)
- Tool and controller hooks — keys your Ingress controller, load balancer, GitOps operator, or custom controller documents (always match that tool’s prefix and schema)
- Structured metadata for automation — JSON or YAML in a single string value for an operator that watches a known annotation key
Put annotations on the object that owns the metadata:
- Controller object only —
metadata.annotationson the Deployment, Ingress, or Namespace describes that resource - Every Pod a controller creates —
spec.template.metadata.annotationson the Pod template when new replicas should inherit the same keys
Do not use annotations when:
- Selectors must match the value — application name, environment, or tier for Services and
kubectl get -lbelong in labels - The value changes every release but routing must stay stable — a new Git SHA each build is an annotation;
app=webstays a label - The data is secret — credentials belong in a Secret, not in plain metadata anyone with read access can see
For a side-by-side label vs annotation table and wrong-vs-right examples, read the labels vs annotations comparison. The sections below show syntax and kubectl annotate once you know annotations fit the job.
Annotation key and value syntax
Annotation keys use the same structure as label keys:
- Optional DNS-style prefix and
/, then a name (for exampleexample.com/owner) - Name portion: at most 63 characters
- Optional prefix: at most 253 characters
- Reserved prefixes include
kubernetes.io/andk8s.io/for system use
Annotation values are always strings in the API, even when they look numeric:
metadata:
annotations:
example.com/documentation: https://docs.example.com/web
example.com/git-commit: a1b2c3d4
example.com/build-id: "20260725.1"
example.com/owner: platform-teamQuote values in YAML when they start with characters YAML might misread. JSON blobs belong in a single string value:
metadata:
annotations:
example.com/config: '{"replicas":3,"region":"eu-west"}'Add annotations in a YAML manifest
Place annotations beside labels under metadata. They apply to the object you create, not automatically to child Pods unless you also set spec.template.metadata.annotations on a controller.
Deployment with owner and build metadata on the controller object:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: annotations-demo
labels:
app: web
annotations:
example.com/owner: platform-team
example.com/build-id: "20260725.1"
example.com/documentation: https://docs.example.com/web
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27-alpinemetadata.annotations on the Deployment describes the Deployment resource. To stamp every Pod the controller creates, add keys under spec.template.metadata.annotations:
spec:
template:
metadata:
annotations:
example.com/build-id: "20260725.1"Changing spec.template.metadata.annotations updates the Pod template. The Deployment rolls out replacement Pods that carry the new annotation; existing Pods keep their old metadata until they are recreated.
Update annotations on existing objects with kubectl
Create a lab namespace and a minimal Deployment if you are following along:
kubectl create namespace annotations-demoThe namespace isolates annotation experiments from other workloads.
kubectl create deployment web --image=nginx:1.27-alpine --replicas=1 -n annotations-demokubectl rollout status deployment/web -n annotations-demo --timeout=60sSample output:
deployment "web" successfully rolled outThe Deployment is ready for annotation changes.
Add an annotation
Add an owner contact annotation on the Deployment:
kubectl annotate deployment web example.com/owner=platform-team -n annotations-demoSample output:
deployment.apps/web annotatedThe default printer reports the operation name annotated when the API accepts the change.
Confirm the key under metadata.annotations:
kubectl get deployment web -n annotations-demo -o jsonpath='{.metadata.annotations}{"\n"}'Sample output:
{"deployment.kubernetes.io/revision":"1","example.com/owner":"platform-team"}deployment.kubernetes.io/revision is system metadata the controller manages. Your custom key sits beside it.
Overwrite an existing annotation
Replace the owner value when the key already exists:
kubectl annotate deployment web example.com/owner=application-team --overwrite -n annotations-demoSample output:
deployment.apps/web annotatedWithout --overwrite, kubectl refuses when the key is already set:
kubectl annotate deployment web example.com/owner=duplicate-test -n annotations-demoSample output:
error: --overwrite is false but found the following declared annotation(s): 'example.com/owner' already has a value (application-team)Pass --overwrite when you intend to change the value.
Remove an annotation
Append - to the key name to delete it. Add a temporary annotation first:
kubectl annotate deployment web example.com/description="Dev web app" -n annotations-demoSample output:
deployment.apps/web annotatedRemove it with the trailing-hyphen syntax:
kubectl annotate deployment web example.com/description- -n annotations-demoSample output:
deployment.apps/web annotatedThe second command deletes example.com/description only. Other annotations such as example.com/owner remain.
Annotate multiple keys or objects
kubectl annotate accepts multiple key=value pairs on one command:
kubectl annotate deployment web example.com/build-id="20260725.1" example.com/documentation=https://docs.example.com/web -n annotations-demoSample output:
deployment.apps/web annotatedTo target several resources at once, pass a label selector. -l app=web chooses which objects to update; it does not make the new annotation selectable with kubectl get -l:
kubectl annotate pods -l app=web example.com/owner=platform-team -n annotations-demoSample output:
pod/web-7b8c57c6d6-7fcbc annotatedThe same command supports --all in a namespace when you intend to annotate every object of that type. Prefer a label selector when you need a narrower target set.
View annotations on objects
List every annotation on one object with the command-specific --list flag:
kubectl annotate deployment web --list -n annotations-demoSample output:
example.com/build-id=20260725.1
example.com/documentation=https://docs.example.com/web
example.com/owner=application-team
deployment.kubernetes.io/revision=1kubectl annotate --list prints one key=value line per annotation on that object.
kubectl describe prints annotations in the metadata section at the top:
kubectl describe deployment web -n annotations-demoSample output (trimmed):
Name: web
Namespace: annotations-demo
Labels: app=web
Annotations: deployment.kubernetes.io/revision: 1
example.com/owner: application-team
Selector: app=webFor scripting, use JSONPath and escape the dot in the annotation prefix; the slash remains unchanged:
kubectl get deployment web -n annotations-demo -o jsonpath='{.metadata.annotations.example\.com/owner}{"\n"}'The path uses example\.com/owner — same pattern as the official kubernetes\.io/hostname example: escape dots in the DNS prefix, not the / before the name.
Sample output:
application-teamkubectl get -o yaml shows the full metadata.annotations map when you need every key at once.
Do not store credentials, tokens, or private keys in annotations. Use Kubernetes Secrets and reference them from the Pod spec.
Troubleshoot Kubernetes annotations
| Symptom | Likely cause | Fix |
|---|---|---|
kubectl annotate says the key already has a value |
Key exists without --overwrite |
Pass --overwrite or remove the key with key- first |
| Annotation missing on new Pods after editing the Deployment | Annotation on Deployment metadata only | Add the key under spec.template.metadata.annotations and roll out |
| JSONPath returns empty for a visible key | Dot in the prefixed key was not escaped | Use example\.com/owner; escape the dot, not the slash |
| API rejects the object after adding annotations | Total annotations exceed 256 KiB | Shorten values or store large data elsewhere |
Service or kubectl get -l ignores the key |
Annotations are not labels | Move selectable metadata to metadata.labels |
What's Next
References
Summary
Annotations store non-identifying metadata under metadata.annotations. Add them in YAML at creation time or with kubectl annotate on live objects; use --overwrite to replace values and append - to the key to remove one. Selectors ignore annotations — use Related guides above when you need labels, selectors, or a labels-vs-annotations comparison.

