πŸ“šBook Signing at KubeCon EU 2026Meet us at Booking.com HQ (Mon 18:30-21:00) & vCluster booth #521 (Tue 24 Mar, 12:30-1:30pm) β€” free book giveaway!RSVP Booking.com Event
Configuration beginner ⏱ 15 minutes K8s 1.28+

Kubernetes Labels and Annotations Best Practices

Implement Kubernetes labels and annotations following best practices. Recommended label keys, organizational conventions, selectors, annotations vs labels

By Luca Berton β€’ β€’ πŸ“– 5 min read

πŸ’‘ 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"
└───────────────────────────────────└──────────────────────────────────────
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.0

Common 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 yaml selector with kubectl get pods --show-labels

Deployment can’t be updated β€” selector is immutable

  • Cause: spec.selector.matchLabels can’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

  1. Use app.kubernetes.io/* labels β€” industry standard, tool-compatible
  2. Labels for selection, annotations for everything else β€” if you won’t select on it, annotate it
  3. Keep labels stable β€” selectors (Services, Deployments) break if labels change
  4. Use prefixed keys for custom labels β€” mycompany.com/team avoids collisions
  5. Include environment, team, version β€” enables filtering and cost attribution
  6. Don’t put secrets in annotations β€” they’re readable by anyone with get access
  7. 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.selector is immutable β€” plan labels before creating Deployments
  • Labels are queryable (-l key=value); annotations are not
#labels #annotations #metadata #best-practices #organization
Luca Berton
Written by Luca Berton

Principal Solutions Architect specializing in Kubernetes, AI/GPU infrastructure, and cloud-native platforms. Author of Kubernetes Recipes and creator of CopyPasteLearn courses.

Kubernetes Recipes book cover

Want More Kubernetes Recipes?

This recipe is from Kubernetes Recipes, our 750-page practical guide with hundreds of production-ready patterns.

Luca Berton Ansible Pilot Ansible by Example Open Empower K8s Recipes Terraform Pilot CopyPasteLearn ProteinLens