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
| Flag | Short | Description |
|---|---|---|
| --overwrite | — | Allow overwriting existing annotation values |
| --all | — | Annotate all resources of the specified type |
| --selector | -l | Filter resources by label selector |
| --dry-run | — | Must be 'none', 'server', or 'client'. Preview without executing |
| --resource-version | — | Only 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' --overwriteAdd an annotation with a URL value
kubectl annotate service my-svc external-dns.alpha.kubernetes.io/hostname=app.example.comAnnotate all pods in a namespace
kubectl annotate pods --all last-reviewed=$(date -I) -n productionWhen 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
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.