Labels, Selectors & Annotations
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.
Based on kubernetes.io/docs/concepts/overview/working-with-objects/labels/.
Labels
Labels are key-value pairs attached to the
metadata section of any Kubernetes object. Each label
consists of a key and a value:
-
Key — optionally prefixed (e.g.
app.kubernetes.io/name). The prefix must be a valid DNS subdomain ≤ 253 characters. The name part is ≤ 63 characters, alphanumeric, dashes, underscores, and dots. - Value — ≤ 63 characters, alphanumeric, dashes, underscores, and dots. May be empty.
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:
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
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:
apiVersion: v1
kind: Service
metadata:
name: payments-api
spec:
selector:
app: payments-api # routes to pods with this label
ports:
- port: 80
targetPort: 8080
Recommended Label Schema
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:
- Annotation values can be arbitrary strings with no length limit.
- They are not used by Kubernetes selectors — controllers never use annotations to find or group resources.
- They are used by external tooling, admission controllers, operators, and CI/CD systems.
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.