kubectl annotate

Update annotations on a resource. Annotations store non-identifying metadata for tools, libraries, and operational information.

kubectl annotate RESOURCE NAME KEY=VALUE [flags]

Common Flags

FlagShortDescription
--overwriteAllow overwriting existing annotation values
--allAnnotate all resources of the specified type
--selector-lFilter resources by label selector
--dry-runMust be 'none', 'server', or 'client'. Preview without executing
--resource-versionOnly update if the resource version matches

Examples

Add an annotation to a deployment

kubectl annotate deployment my-app description='Main application server'

Remove an annotation

kubectl annotate deployment my-app description-

Overwrite an existing annotation

kubectl annotate deployment my-app description='Updated description' --overwrite

Add an annotation with a URL value

kubectl annotate service my-svc external-dns.alpha.kubernetes.io/hostname=app.example.com

Annotate all pods in a namespace

kubectl annotate pods --all last-reviewed=$(date -I) -n production

When to Use kubectl annotate

kubectl annotate manages annotations on Kubernetes resources. Unlike labels, annotations are not used for selection or querying — they store supplementary metadata consumed by tools, controllers, and operators.

Adding Annotations

# Simple annotation
kubectl annotate pod my-pod description="Frontend web server"

# Tool-specific annotation
kubectl annotate ingress my-ingress nginx.ingress.kubernetes.io/rewrite-target=/

# Annotation with special characters (quote the value)
kubectl annotate service my-svc 'config.json={"timeout": 30, "retries": 3}'

Removing Annotations

Append a minus sign to the key:

kubectl annotate pod my-pod description-
kubectl annotate ingress my-ingress nginx.ingress.kubernetes.io/rewrite-target-

Common Annotation Use Cases

Ingress Controller Configuration

# NGINX ingress annotations
kubectl annotate ingress my-ingress \
  nginx.ingress.kubernetes.io/ssl-redirect="true" \
  nginx.ingress.kubernetes.io/proxy-body-size="50m" \
  nginx.ingress.kubernetes.io/rate-limit="100"

External DNS

# Automatic DNS registration
kubectl annotate service my-svc \
  external-dns.alpha.kubernetes.io/hostname="app.example.com"

Prometheus Scraping

# Enable Prometheus scraping
kubectl annotate pod my-pod \
  prometheus.io/scrape="true" \
  prometheus.io/port="8080" \
  prometheus.io/path="/metrics"

cert-manager TLS

# Request TLS certificate via cert-manager
kubectl annotate ingress my-ingress \
  cert-manager.io/cluster-issuer="letsencrypt-prod"

Annotations vs Labels

| Feature | Labels | Annotations | |---------|--------|-------------| | Purpose | Identification and selection | Non-identifying metadata | | Queryable | Yes (via -l selectors) | No | | Size limit | 63 chars per value | 256KB per value | | Used by | Services, Deployments, RBAC | Tools, controllers, humans | | Characters | Restricted set | Arbitrary strings | | Examples | app=nginx, tier=frontend | description="...", tool configs |

Operational Annotations

Track operational metadata:

# Record who last modified a resource
kubectl annotate deployment my-app last-modified-by="jane@company.com" --overwrite

# Record the change reason
kubectl annotate deployment my-app change-reason="Scale up for holiday traffic" --overwrite

# Record the ticket number
kubectl annotate deployment my-app ticket="JIRA-1234" --overwrite

# Timestamp
kubectl annotate deployment my-app last-updated="$(date -u +%Y-%m-%dT%H:%M:%SZ)" --overwrite

Bulk Annotations

# Annotate all deployments in a namespace
kubectl annotate deployments --all team=backend -n production

# Annotate by label selector
kubectl annotate pods -l app=nginx monitored-by=datadog

# Annotate across types
for resource in deployment service configmap; do
  kubectl annotate "$resource" my-app owner=platform-team --overwrite
done

Viewing Annotations

# Show all annotations
kubectl describe deployment my-app | grep -A 20 "Annotations"

# Extract specific annotation
kubectl get deployment my-app -o jsonpath='{.metadata.annotations.description}'

# List all annotations as JSON
kubectl get deployment my-app -o json | jq '.metadata.annotations'

The kubectl.kubernetes.io/last-applied-configuration Annotation

When you use kubectl apply, Kubernetes automatically stores the last applied configuration as an annotation:

# View the last-applied-configuration
kubectl get deployment my-app -o jsonpath='{.metadata.annotations.kubectl\.kubernetes\.io/last-applied-configuration}' | jq .

This annotation enables three-way merge during subsequent kubectl apply operations. It records what was last applied so kubectl can detect manual changes.

Annotations in Manifests

Annotations are commonly defined in YAML manifests:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  annotations:
    description: "Main application deployment"
    team: "platform"
    prometheus.io/scrape: "true"
spec:
  # ...

Scripting with Annotations

Use annotations to implement custom workflows:

#!/bin/bash
# Mark deployments for review
kubectl annotate deployments --all needs-review=true -n staging

# Process reviewed deployments
for deploy in $(kubectl get deployments -n staging -o name); do
  reviewed=$(kubectl get "$deploy" -n staging -o jsonpath='{.metadata.annotations.needs-review}')
  if [ "$reviewed" = "true" ]; then
    echo "Needs review: $deploy"
  fi
done

Annotations provide a flexible metadata layer that bridges the gap between Kubernetes resources and the tools that manage them.

Interview Questions About This Command

What are annotations and how do they differ from labels?
Annotations store arbitrary metadata that cannot be used for selection. Labels are for identification and querying. Annotations hold things like descriptions, tool config, and build info.
Give examples of annotations used by Kubernetes tools.
Ingress controllers use annotations for routing config. cert-manager reads annotations for TLS. Prometheus uses annotations for scrape config.
Is there a size limit for annotation values?
Individual annotations can hold up to 256KB of data. The total metadata (labels + annotations) is limited by the etcd value size limit (typically 1.5MB).

Common Mistakes

  • Using annotations for data that should be labels — if you need to select or filter resources, use labels instead.
  • Forgetting --overwrite when updating an existing annotation, which causes an error.
  • Storing large data in annotations that should be in ConfigMaps or Secrets instead.

Related Commands