Kubernetes Labels and Annotations Best Practices
Implement Kubernetes labels and annotations following best practices. Recommended label keys, organizational conventions, selectors, annotations vs labels
π‘ Quick Answer: Labels are for identifying and selecting resources (used by services, deployments, scheduling). Annotations are for non-identifying metadata (build info, descriptions, tool config). Use the recommended
app.kubernetes.io/*label keys. Labels are queryable with selectors; annotations are not.
The Problem
- No standard for labeling resources β inconsistent across teams
- Canβt select resources effectively without proper labels
- Confusion between when to use labels vs annotations
- Services canβt find pods without matching label selectors
- Hard to identify resource ownership, version, and purpose in large clusters
The Solution
Labels vs Annotations
Labels: Annotations:
βββ Used by selectors βββ NOT used by selectors
βββ Identify resources βββ Attach non-identifying metadata
βββ Max 63 chars value βββ Max 256KB total size
βββ Used by: Services, Deployments, βββ Used by: tools, humans, automation
β HPA, NetworkPolicy, scheduling β
βββ Examples: βββ Examples:
β app: frontend β description: "Main API service"
β version: v2.1.0 β build.url: "https://ci.example.com/123"
β environment: production β kubectl.kubernetes.io/last-applied
β tier: backend β prometheus.io/scrape: "true"
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββRecommended Labels (app.kubernetes.io)
metadata:
labels:
# Standard Kubernetes recommended labels
app.kubernetes.io/name: "api-server" # Application name
app.kubernetes.io/instance: "api-server-prod" # Unique instance
app.kubernetes.io/version: "2.1.0" # Application version
app.kubernetes.io/component: "backend" # Component within app
app.kubernetes.io/part-of: "e-commerce" # Higher-level application
app.kubernetes.io/managed-by: "helm" # Tool managing this
# Helm standard labels
helm.sh/chart: "api-server-1.5.0"
# Custom organizational labels
team: "platform"
cost-center: "engineering"
environment: "production"Common Label Patterns
# Service selector (must match pod labels)
apiVersion: v1
kind: Service
metadata:
name: api-server
spec:
selector:
app.kubernetes.io/name: api-server
app.kubernetes.io/instance: api-server-prod
---
# Deployment with recommended labels
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
labels:
app.kubernetes.io/name: api-server
app.kubernetes.io/version: "2.1.0"
app.kubernetes.io/managed-by: helm
spec:
selector:
matchLabels:
app.kubernetes.io/name: api-server
app.kubernetes.io/instance: api-server-prod
template:
metadata:
labels:
app.kubernetes.io/name: api-server
app.kubernetes.io/instance: api-server-prod
app.kubernetes.io/version: "2.1.0"
app.kubernetes.io/component: backend
spec:
containers:
- name: api
image: registry.example.com/api:2.1.0Common Annotations
metadata:
annotations:
# Informational
description: "Main customer-facing API service"
owner: "platform-team@example.com"
documentation: "https://wiki.example.com/api-server"
# CI/CD
build.url: "https://ci.example.com/builds/12345"
git.commit: "abc123def456"
git.branch: "main"
# Prometheus scraping
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
prometheus.io/path: "/metrics"
# Ingress configuration
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: letsencrypt-prod
# Deployment rollout trigger
checksum/config: "sha256:abc123..."
# Linkerd/Istio injection
linkerd.io/inject: enabled
sidecar.istio.io/inject: "true"Query with Label Selectors
# Equality-based
kubectl get pods -l app.kubernetes.io/name=api-server
kubectl get pods -l environment=production,tier=backend
# Set-based
kubectl get pods -l 'environment in (production, staging)'
kubectl get pods -l 'tier notin (frontend)'
kubectl get pods -l '!canary' # Pods WITHOUT canary label
# Combine with output
kubectl get pods -l app.kubernetes.io/part-of=e-commerce \
--show-labels -o wide
# Count resources by label
kubectl get pods -l team=platform --no-headers | wc -l
# Add/remove labels
kubectl label pods my-pod-xxx environment=production
kubectl label pods my-pod-xxx environment- # Remove label
kubectl label pods --all tier=backend -n production # All pods
# Add annotation
kubectl annotate deployment api-server description="Main API"Label Naming Rules
Key format: [prefix/]name
βββ prefix: optional DNS subdomain (max 253 chars)
βββ name: required (max 63 chars, alphanumeric + - _ .)
βββ Must start and end with alphanumeric
Value format:
βββ Max 63 characters
βββ Alphanumeric + - _ .
βββ Can be empty string
βββ Must start and end with alphanumeric (if non-empty)
Reserved prefixes:
βββ kubernetes.io/ β Kubernetes core components
βββ k8s.io/ β Kubernetes SIG projects
βββ app.kubernetes.io/ β Recommended application labels
βββ *.example.com/ β Your organization
Examples:
β
app: frontend
β
app.kubernetes.io/name: api-server
β
mycompany.com/team: platform
β
version: 2.1.0-beta.1
β app: "this value is way too long and exceeds sixty-three characters limit"
β 123-invalid: (can't start with number for key name)Common Issues
Service has no endpoints (selector doesnβt match)
- Cause: Service selector labels donβt match pod labels exactly
- Fix: Compare
kubectl get svc -o yamlselector withkubectl get pods --show-labels
Deployment canβt be updated β selector is immutable
- Cause:
spec.selector.matchLabelscanβt be changed after creation - Fix: Delete and recreate deployment; or create new deployment with different name
Too many labels causing etcd storage issues
- Cause: Hundreds of labels per object exceeding etcd object size limit
- Fix: Move non-identifying data to annotations; limit labels to whatβs needed for selection
Best Practices
- Use
app.kubernetes.io/*labels β industry standard, tool-compatible - Labels for selection, annotations for everything else β if you wonβt select on it, annotate it
- Keep labels stable β selectors (Services, Deployments) break if labels change
- Use prefixed keys for custom labels β
mycompany.com/teamavoids collisions - Include environment, team, version β enables filtering and cost attribution
- Donβt put secrets in annotations β theyβre readable by anyone with get access
- Document your labeling convention β publish a team/org standard
Key Takeaways
- Labels: identify + select resources (used by Services, HPA, NetworkPolicy, scheduling)
- Annotations: non-identifying metadata (tool config, descriptions, build info)
- Use
app.kubernetes.io/*recommended labels for interoperability - Label values max 63 chars; annotation values max 256KB
- Service selectors MUST match pod labels exactly β no endpoints otherwise
spec.selectoris immutable β plan labels before creating Deployments- Labels are queryable (
-l key=value); annotations are not

Recommended
Kubernetes Recipes β The Complete Book100+ production-ready patterns with detailed explanations, best practices, and copy-paste YAML. Everything in one place.
Get the Book βLearn by Doing
CopyPasteLearn β Hands-on Cloud & DevOps CoursesMaster Kubernetes, Ansible, Terraform, and MLOps with interactive, copy-paste-run lessons. Start free.
Browse Courses βπ Deepen Your Skills β Hands-on Courses
Courses by CopyPasteLearn.com β Learn IT by Doing
