Skip to main content
RunBook Academy

KubernetesV · Kubernetes Objects and MetadataKubernetes objects and metadata

Labels and annotations — identifying and describing objects

Intermediate⏱ ~16 minkubectl

What you'll learn

  • Distinguish labels (selection) from annotations (description)
  • Apply Kubernetes's label key/value rules (prefix, length, character set)
  • Use label selectors effectively with services, deployments, network policies, and quotas
  • Apply label hygiene patterns in production: app.kubernetes.io/*, ownership, environment

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

Labels and annotations are both key-value metadata on objects. They differ in a fundamental way: labels drive selection; annotations hold non-identifying metadata. This lesson covers both, the rules for valid keys/values, and the production patterns that keep them useful at scale.

Labels: the selected kind of metadata

Labels are key-value pairs that identify and group objects. The defining property: labels are queryable. Services, Deployments, NetworkPolicies, and other controllers use label selectors to choose which objects they apply to.

metadata:
  labels:
    app.kubernetes.io/name: web
    app.kubernetes.io/component: frontend
    app.kubernetes.io/part-of: checkout
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/instance: web-prod
    environment: prod
    team: payments

Rules:

  • Key: optional prefix (DNS subdomain, ≤ 253 chars) + / + name (≤ 63 chars). Without a prefix, the key is in the user’s domain.
  • Value: ≤ 63 chars; alphanumeric, dashes, underscores, dots.
  • Each object can have many labels.
  • Labels can be added, modified, or removed at any time.
# Add a label
kubectl label pod web-abc env=prod

# Update a label
kubectl label pod web-abc env=staging --overwrite

# Remove a label
kubectl label pod web-abc env-

Production labels are typically prefixed with app.kubernetes.io/ (a Kubernetes-recommended convention) or the organisation’s domain (acme.example.com/team).

Annotations: the descriptive kind of metadata

Annotations are also key-value pairs but they are not queryable. They hold non-identifying information:

metadata:
  annotations:
    description: "Production web tier for checkout"
    contact: "team-payments@example.com"
    runbook: "https://wiki.example.com/runbooks/web-prod"
    prometheus.io/scrape: "true"
    prometheus.io/port: "9090"
    kubernetes.io/change-cause: "Image bump to 1.27.2"

Rules:

  • Key: same as labels (prefix + name); but annotations can use a wider character set
  • Value: any string (≤ 256 KB total per object)
  • Annotations are not indexed in etcd
  • Annotations are meant for tools, not for selection

Common annotation uses:

  • Tool integration (Prometheus scrape config, Vault references, cert-manager)
  • Documentation (runbook URLs, contact info)
  • Build information (commit SHA, build timestamp)
  • Lifecycle annotations (kubernetes.io/change-cause)

Label selectors

A label selector is a query that returns objects matching the labels. Two forms:

Equality-based

app = web
env != dev

Match labels with the exact value, or exclude them.

Set-based

app in (web, api)
env notin (dev)
!partition

Match labels in a set, or exclude a set. The ! prefix selects objects without the label.

Used in:

  • Service.spec.selector — which Pods the Service routes to
  • Deployment.spec.selector — which Pods the Deployment owns
  • NetworkPolicy.spec.podSelector — which Pods the policy applies to
  • kubectl get -l — query objects from the command line
  • kubectl delete -l — delete by selector
# All Pods with app=web in any namespace
kubectl get pods -A -l app=web

# Pods in prod that are not part of the legacy stack
kubectl get pods -n prod -l environment=prod,stack!=legacy

# Pods labeled with the team annotation
kubectl get pods -A -l 'team in (payments,search)'

Production label conventions

A common pattern uses the Kubernetes-recommended app.kubernetes.io/* labels:

LabelPurpose
app.kubernetes.io/nameThe application’s name
app.kubernetes.io/instanceA specific instance of the application
app.kubernetes.io/versionThe current version (semver)
app.kubernetes.io/componentA logical component within the architecture
app.kubernetes.io/part-ofThe higher-level application this belongs to
app.kubernetes.io/managed-byThe tool managing the object (helm, kustomize, argocd)
metadata:
  labels:
    app.kubernetes.io/name: web
    app.kubernetes.io/instance: web-prod
    app.kubernetes.io/version: "1.27.2"
    app.kubernetes.io/component: frontend
    app.kubernetes.io/part-of: checkout
    app.kubernetes.io/managed-by: argocd

Additional production conventions:

    environment: prod
    tier: frontend
    team: payments
    cost-center: "12345"
    compliance: pci-dss
    # for service mesh
    app: web
    # for tooling
    owner: team-payments

The convention varies by team; the discipline is to have one and apply it consistently.

How labels drive object discovery

# Find all objects belonging to the checkout application
kubectl get all -A -l app.kubernetes.io/part-of=checkout
# Find all objects managed by Helm
kubectl get all -A -l app.kubernetes.io/managed-by=helm
# Find all resources for the payments team's prod environment
kubectl get all -n payments -l environment=prod,team=payments
# Iterate over matching Pods in a script
for pod in $(kubectl get pods -l app=web -o name); do
  kubectl logs $pod --since=10m
done

A consistent label scheme is the difference between “I can find this object” and “I cannot find this object”.

Label selectors in controllers

Controllers use label selectors to choose which objects they own. The Deployment controller:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:1.27.1

The Deployment’s selector.matchLabels must match the labels in template.metadata.labels. If they don’t, the API server rejects the manifest at creation.

A common operational mistake is changing a Deployment’s spec.selector after creation — the API server rejects it (field is immutable). To change the selector, delete the Deployment and recreate it (with the data being lost; this is why the selector is immutable).

The Service uses the same selector pattern:

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web   # routes to Pods with this label
  ports:
  - port: 80
    targetPort: 8080

If the Service’s selector doesn’t match any Pods, the Service has no Endpoints; traffic to the Service is dropped.

Annotations vs labels: a decision framework

NeedUse
Select objectsLabel
Group objects (e.g., by team, env)Label
Identify a specific instanceLabel
Document runbook URLAnnotation
Tool-specific config (Prometheus scrape)Annotation
Build/deployment metadataAnnotation
Sensitive data (PII, secrets)Neither — use a Secret

Labels and annotations are both visible to anyone with read access to the object. Do not put secrets in either; use a Secret or external secret manager.

Label cardinality and performance

The number of unique label values affects API server and etcd performance. A label with high cardinality (e.g., a unique value per request) can:

  • Bloat etcd storage
  • Increase watch event volume
  • Slow down list operations

Production rules:

  • Avoid labels with unbounded cardinality (timestamps, IDs)
  • Prefer low-cardinality labels (env, tier, team)
  • For high-cardinality identifiers, use annotations or external observability systems

Cross-course references

  • The Linux course part XXVII-Linux-Auth covers identity/group concepts that map onto label-based grouping.
  • The Observability course part IX-Observability-Exporters covers Prometheus label handling — similar trade-offs to Kubernetes labels.
  • The Linux course part XXIX-Linux-Hardening covers classification schemes (security levels, compliance) that map onto label conventions.
  • The Docker course part XXXVIII-Docker-Secrets covers secret handling; labels and annotations are not for secrets.

Quiz

Knowledge check · 4 questions

  1. Q1. Which of the following is a valid Kubernetes label?

  2. Q2. Annotations are queryable via kubectl selectors, similar to labels.

  3. Q3. A Service has been deployed but `kubectl get endpoints web` returns empty. The Pods are running and have `app: web` labels. The Service's selector is `app: web`. Diagnose and remediate.

    Service: ```yaml apiVersion: v1 kind: Service metadata: name: web namespace: prod spec: selector: app: web ports: - port: 80 targetPort: 8080 ``` Pods: ``` $ kubectl get pods -n prod -l app=web NAME READY STATUS RESTARTS AGE web-abc-1 1/1 Running 0 5m web-abc-2 1/1 Running 0 5m web-abc-3 1/1 Running 0 5m ``` Endpoints: ``` $ kubectl get endpoints web -n prod NAME ENDPOINTS AGE web <none> 5m ``` Full Pod labels (one of them): ```yaml metadata: labels: app: web app.kubernetes.io/name: web app.kubernetes.io/instance: web-prod ```

  4. Q4. Describe the discipline of label hygiene in production. Name three production labels every object should carry, and explain why high cardinality is dangerous.

Passing score: 75%. Answers are checked in this browser.

Production discipline

  • Adopt a label convention (e.g., app.kubernetes.io/*) and apply it to every object.
  • Use labels for selection, annotations for documentation and tooling. Never confuse the two.
  • Keep cardinality low; avoid labels with unbounded unique values.
  • Treat metadata.labels as the Service/Deployment selector’s contract — they must match exactly.
  • Document the label convention and enforce it with admission policies (e.g., ValidatingAdmissionPolicy that requires certain labels).