Foundations organization

Labels, Selectors & Annotations

● Beginner ⏱ 10 min read organization

Labels and annotations are both key-value pairs attached to Kubernetes objects, but they serve different purposes. Labels are identifying metadata used by Kubernetes itself to select and group resources. Annotations hold non-identifying metadata for tooling, operators, and humans. Understanding the difference is fundamental to how Deployments, Services, and other controllers manage pods.

Labels

Labels are key-value pairs attached to the metadata section of any Kubernetes object. Each label consists of a key and a value:

pod-with-labels.yaml
apiVersion: v1
kind: Pod
metadata:
  name: payments-api
  labels:
    app.kubernetes.io/name: payments
    app.kubernetes.io/version: "2.4.1"
    app.kubernetes.io/component: api
    environment: production
    tier: backend
spec:
  containers:
  - name: api
    image: payments-api:2.4.1

Labels are inert on their own — they become powerful when selectors reference them.

Selectors

A selector is a query expression that matches objects by their labels. Kubernetes uses selectors internally (e.g. a Service routes traffic to pods whose labels match spec.selector) and exposes them via kubectl for ad-hoc queries.

Equality-based selectors

Match resources where a label equals (or does not equal) a value:

# pods where environment=production AND tier=backend
kubectl get pods -l environment=production,tier=backend

# pods where environment is NOT production
kubectl get pods -l environment!=production

Set-based selectors

More expressive — match by membership in a set of values:

# pods where environment is one of staging or production
kubectl get pods -l 'environment in (staging, production)'

# pods where tier is NOT frontend
kubectl get pods -l 'tier notin (frontend)'

# pods that have the label "canary" (any value)
kubectl get pods -l canary

# pods that do NOT have the label "canary"
kubectl get pods -l '!canary'

Selectors in manifests

Controllers use selector fields in their spec. A Deployment's pod template must carry matching labels:

deployment-selector.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payments-api
spec:
  selector:
    matchLabels:
      app: payments-api       # selector — must match pod template
  template:
    metadata:
      labels:
        app: payments-api     # pod labels — must match selector
    spec:
      containers:
      - name: api
        image: payments-api:2.4.1
⚠️
Selector is immutable

Once a Deployment (or ReplicaSet, StatefulSet, DaemonSet) is created, its spec.selector cannot be changed. To change the selector you must delete and recreate the resource.

A Service also uses a selector to determine which pods receive traffic:

service-selector.yaml
apiVersion: v1
kind: Service
metadata:
  name: payments-api
spec:
  selector:
    app: payments-api   # routes to pods with this label
  ports:
  - port: 80
    targetPort: 8080

Kubernetes defines a set of well-known labels under the app.kubernetes.io/ prefix. Using them consistently makes your resources interoperable with dashboards, operators, and tooling:

Label Example Meaning
app.kubernetes.io/name payments Name of the application
app.kubernetes.io/version 2.4.1 Current version of the app
app.kubernetes.io/component api Component within an application
app.kubernetes.io/part-of ecommerce Larger application this is part of
app.kubernetes.io/managed-by helm Tool used to manage the resource
app.kubernetes.io/instance payments-prod Unique instance name (e.g. release name)

Annotations

Annotations are key-value pairs intended for non-identifying metadata. Unlike labels:

deployment-annotations.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payments-api
  annotations:
    # Deployment tracking
    deployment.kubernetes.io/revision: "4"
    # Prometheus scraping hints
    prometheus.io/scrape: "true"
    prometheus.io/port: "8080"
    prometheus.io/path: "/metrics"
    # Cert-manager configuration
    cert-manager.io/cluster-issuer: letsencrypt-prod
    # Human-readable notes
    description: "Payment processing API — see runbook at wiki/payments"
    git-commit: "a3f4c92"

Common real-world uses of annotations include:

Tool Annotation example
Ingress controllers nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager cert-manager.io/cluster-issuer: letsencrypt-prod
Prometheus prometheus.io/scrape: "true"
Argo CD argocd.argoproj.io/sync-wave: "1"
Kubernetes itself kubectl.kubernetes.io/last-applied-configuration

Field Selectors

Field selectors filter resources by object fields (not labels) — useful for finding pods in a specific phase or bound to a specific node:

# pods that are currently Running
kubectl get pods --field-selector status.phase=Running

# pods scheduled on a specific node
kubectl get pods --field-selector spec.nodeName=worker-1

# services that are not ClusterIP
kubectl get services --field-selector spec.type!=ClusterIP

Field selectors support only equality operators (=, ==, !=) and the available fields vary by resource type. They are less commonly used than label selectors but invaluable for debugging.