Kubernetes Annotations with Examples

Learn what Kubernetes annotations are, add them in YAML or with kubectl annotate, view and remove annotation keys, and choose annotations instead of labels for non-identifying metadata.

Published

Updated

Read time 8 min read

Reviewed byDeepak Prasad

Kubernetes annotations on Deployment metadata for build IDs, owners, and tool metadata
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 onlymetadata.annotations on the Deployment, Ingress, or Namespace describes that resource
  • Every Pod a controller createsspec.template.metadata.annotations on 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 -l belong in labels
  • The value changes every release but routing must stay stable — a new Git SHA each build is an annotation; app=web stays 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 example example.com/owner)
  • Name portion: at most 63 characters
  • Optional prefix: at most 253 characters
  • Reserved prefixes include kubernetes.io/ and k8s.io/ for system use

Annotation values are always strings in the API, even when they look numeric:

yaml
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-team

Quote values in YAML when they start with characters YAML might misread. JSON blobs belong in a single string value:

yaml
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:

yaml
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-alpine

metadata.annotations on the Deployment describes the Deployment resource. To stamp every Pod the controller creates, add keys under spec.template.metadata.annotations:

yaml
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:

bash
kubectl create namespace annotations-demo

The namespace isolates annotation experiments from other workloads.

bash
kubectl create deployment web --image=nginx:1.27-alpine --replicas=1 -n annotations-demo
bash
kubectl rollout status deployment/web -n annotations-demo --timeout=60s

Sample output:

output
deployment "web" successfully rolled out

The Deployment is ready for annotation changes.

Add an annotation

Add an owner contact annotation on the Deployment:

bash
kubectl annotate deployment web example.com/owner=platform-team -n annotations-demo

Sample output:

output
deployment.apps/web annotated

The default printer reports the operation name annotated when the API accepts the change.

Confirm the key under metadata.annotations:

bash
kubectl get deployment web -n annotations-demo -o jsonpath='{.metadata.annotations}{"\n"}'

Sample output:

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:

bash
kubectl annotate deployment web example.com/owner=application-team --overwrite -n annotations-demo

Sample output:

output
deployment.apps/web annotated

Without --overwrite, kubectl refuses when the key is already set:

bash
kubectl annotate deployment web example.com/owner=duplicate-test -n annotations-demo

Sample output:

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:

bash
kubectl annotate deployment web example.com/description="Dev web app" -n annotations-demo

Sample output:

output
deployment.apps/web annotated

Remove it with the trailing-hyphen syntax:

bash
kubectl annotate deployment web example.com/description- -n annotations-demo

Sample output:

output
deployment.apps/web annotated

The 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:

bash
kubectl annotate deployment web example.com/build-id="20260725.1" example.com/documentation=https://docs.example.com/web -n annotations-demo

Sample output:

output
deployment.apps/web annotated

To 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:

bash
kubectl annotate pods -l app=web example.com/owner=platform-team -n annotations-demo

Sample output:

output
pod/web-7b8c57c6d6-7fcbc annotated

The 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:

bash
kubectl annotate deployment web --list -n annotations-demo

Sample output:

output
example.com/build-id=20260725.1
example.com/documentation=https://docs.example.com/web
example.com/owner=application-team
deployment.kubernetes.io/revision=1

kubectl annotate --list prints one key=value line per annotation on that object.

kubectl describe prints annotations in the metadata section at the top:

bash
kubectl describe deployment web -n annotations-demo

Sample output (trimmed):

output
Name:                   web
Namespace:              annotations-demo
Labels:                 app=web
Annotations:            deployment.kubernetes.io/revision: 1
                        example.com/owner: application-team
Selector:               app=web

For scripting, use JSONPath and escape the dot in the annotation prefix; the slash remains unchanged:

bash
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:

output
application-team

kubectl 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.


Frequently Asked Questions

1. Can selectors match annotations?

No. kubectl -l, Service selectors, and Deployment spec.selector match labels only. Some controllers read specific annotation keys they document, but annotations are not part of the standard label selector grammar.

2. Can I store secrets in annotations?

No. Annotations are plain metadata on the object and are visible to anyone with read access to that resource. Store credentials in a Kubernetes Secret and mount or reference them from the Pod spec.

3. What is the difference between labels and annotations?

Labels identify objects for selectors and kubectl get -l. Annotations hold descriptive metadata selectors ignore. See Kubernetes labels vs annotations for a full comparison; this article covers annotation procedures only.

4. Why does kubectl annotate refuse to change an existing value?

kubectl annotate protects existing keys unless you pass --overwrite. Add --overwrite when you intend to replace an annotation value on a live object.

5. Is there a size limit on annotations?

Yes. The combined size of all annotation keys and values on one object must not exceed 256 KiB. Keep large blobs out of annotations or store them elsewhere and reference a short URL or ID instead.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)