Labels and Selectors
Organize and query Kubernetes resources using labels and selectors. Essential for managing resources at scale.
Labels and Selectors
In the previous tutorial, we organized our cluster into tidy namespaces — like giving each team their own floor in the office building. But now we need a way to find stuff within those floors.
Labels are the glue that holds Kubernetes together. Seriously. They're how Services find Pods, how Deployments manage ReplicaSets, and how you keep track of thousands of resources without losing your mind. If Kubernetes were a library, labels would be the Dewey Decimal System.
What are Labels?
Labels are just key-value pairs attached to Kubernetes objects. That's it. They're sticky notes you slap on your resources:
metadata:
labels:
app: nginx
environment: production
team: frontend
version: v1.2.0
"Can I label things whatever I want?"
Yep! Kubernetes doesn't enforce any meaning — that's entirely up to you. You could label something vibes: immaculate and Kubernetes wouldn't bat an eye. (Please don't do this in production though.)
Add Labels to Resources
In YAML
apiVersion: v1
kind: Pod
metadata:
name: nginx-prod
labels:
app: nginx
environment: production
tier: frontend
spec:
containers:
- name: nginx
image: nginx
Imperatively
Add labels to existing resources on the fly:
kubectl label pod nginx-prod release=stable
Overwrite an existing label:
kubectl label pod nginx-prod environment=staging --overwrite
Remove a label (the minus sign is the magic trick — sneaky, right?):
kubectl label pod nginx-prod release-
View Labels
Let's see what labels are on our stuff.
Show labels in output
kubectl get pods --show-labels
NAME READY STATUS LABELS
nginx-prod 1/1 Running app=nginx,environment=production,tier=frontend
nginx-dev 1/1 Running app=nginx,environment=development,tier=frontend
redis-prod 1/1 Running app=redis,environment=production,tier=backend
Show specific labels as columns
Want a cleaner view? Show specific labels as table columns:
kubectl get pods -L app,environment
NAME READY STATUS APP ENVIRONMENT
nginx-prod 1/1 Running nginx production
nginx-dev 1/1 Running nginx development
redis-prod 1/1 Running redis production
Selectors
Okay, now the fun part. Labels are great, but they're useless without a way to query them. That's where selectors come in. They're like SQL queries for your cluster.
Two types:
Equality-Based Selectors
The simple stuff — exact matches:
# Pods where environment equals production
kubectl get pods -l environment=production
# Pods where environment does NOT equal production
kubectl get pods -l environment!=production
Set-Based Selectors
When you need more power — match against sets of values:
# Pods where environment is production OR staging
kubectl get pods -l 'environment in (production, staging)'
# Pods where environment is NOT development
kubectl get pods -l 'environment notin (development)'
# Pods that have the 'app' label (any value)
kubectl get pods -l app
# Pods that don't have the 'temporary' label
kubectl get pods -l '!temporary'
That last one is sneaky useful. "Show me everything that's NOT temporary." Love it.
Combine Selectors
Multiple conditions (AND logic):
kubectl get pods -l 'app=nginx,environment=production'
kubectl get pods -l 'app=nginx,environment in (production, staging)'
Practical Example
Let's get our hands dirty. We'll create several pods with different labels and then query the heck out of them:
# pods.yaml
apiVersion: v1
kind: Pod
metadata:
name: web-prod-1
labels:
app: web
environment: production
version: v1
spec:
containers:
- name: nginx
image: nginx
---
apiVersion: v1
kind: Pod
metadata:
name: web-prod-2
labels:
app: web
environment: production
version: v2
spec:
containers:
- name: nginx
image: nginx
---
apiVersion: v1
kind: Pod
metadata:
name: web-dev
labels:
app: web
environment: development
version: v2
spec:
containers:
- name: nginx
image: nginx
---
apiVersion: v1
kind: Pod
metadata:
name: api-prod
labels:
app: api
environment: production
version: v1
spec:
containers:
- name: nginx
image: nginx
Apply:
kubectl apply -f pods.yaml
Now let's ask some questions:
# All web pods
kubectl get pods -l app=web
# Production pods
kubectl get pods -l environment=production
# Web pods in production
kubectl get pods -l 'app=web,environment=production'
# V2 pods in any environment
kubectl get pods -l version=v2
# Web or api pods
kubectl get pods -l 'app in (web, api)'
How cool is that? It's like having a super-powered search engine for your cluster.
How Kubernetes Uses Labels
"Okay, labels are neat for querying. But is that all they're good for?"
Oh no, my friend. Labels aren't just for your organization — Kubernetes itself uses them to wire everything together. This is the really important part.
Services Find Pods
A Service uses selectors to find which Pods to route traffic to:
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector: # Service matches pods with these labels
app: web
environment: production
ports:
- port: 80
This Service routes traffic to any Pod with app=web AND environment=production. If a Pod doesn't have both labels? Sorry, no traffic for you. The bouncer checks both stamps.
Deployments Manage Pods
Deployments use selectors to know which Pods belong to them:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
spec:
replicas: 3
selector:
matchLabels: # Deployment manages pods with these labels
app: web
template:
metadata:
labels: # Pods get these labels
app: web
environment: production
spec:
containers:
- name: nginx
image: nginx
The selector.matchLabels must match template.metadata.labels. If they don't match, Kubernetes will reject your YAML faster than you can say "label mismatch error."
matchExpressions
For more complex selection in Deployments — when simple equality isn't enough:
spec:
selector:
matchLabels:
app: web
matchExpressions:
- key: environment
operator: In
values:
- production
- staging
- key: version
operator: NotIn
values:
- v1
Operators: In, NotIn, Exists, DoesNotExist
Recommended Labels
Kubernetes suggests a set of standard labels (these are just suggestions, not requirements — but they're really good ones):
| Label | Description | Example |
|---|---|---|
app.kubernetes.io/name | Application name | nginx |
app.kubernetes.io/instance | Instance identifier | nginx-prod |
app.kubernetes.io/version | Version | 1.19.0 |
app.kubernetes.io/component | Component type | frontend |
app.kubernetes.io/part-of | Higher-level app | online-store |
app.kubernetes.io/managed-by | Tool managing this | helm |
Example:
metadata:
labels:
app.kubernetes.io/name: nginx
app.kubernetes.io/instance: nginx-production
app.kubernetes.io/version: "1.25.0"
app.kubernetes.io/component: frontend
app.kubernetes.io/part-of: online-store
app.kubernetes.io/managed-by: kubectl
These become super valuable at scale when multiple teams are deploying similar apps and someone asks "wait, whose nginx is this?"
Annotations vs Labels
"What if I want to attach metadata that isn't for selection?"
That's what annotations are for! Labels are for identification and selection. Annotations are for everything else — like the notes you write on the back of a sticky note.
Labels: Used by selectors, limited characters, for organization
labels:
app: nginx
environment: prod
Annotations: Not used by selectors, can store larger data, for metadata
annotations:
description: "Main web server for the storefront"
owner: "frontend-team@company.com"
git-commit: "abc123def456"
prometheus.io/scrape: "true"
Add Annotations
kubectl annotate pod nginx-prod description="Production web server"
Bulk Operations with Selectors
Here's where selectors get really powerful. You can do things to entire groups of resources at once:
# Delete all development pods
kubectl delete pods -l environment=development
# Scale all web deployments
kubectl scale deployment -l app=web --replicas=5
# Get logs from all frontend pods
kubectl logs -l tier=frontend --all-containers
# Execute command on all nginx pods
kubectl exec -l app=nginx -- nginx -s reload
That kubectl delete pods -l environment=development is both terrifyingly powerful and incredibly useful. Use wisely.
Node Selectors
"Can I control which node my Pod runs on?"
Absolutely! Nodes have labels too, and you can use them to schedule Pods on specific nodes.
Label a node:
kubectl label node minikube disktype=ssd
Schedule Pods to that node:
apiVersion: v1
kind: Pod
metadata:
name: ssd-pod
spec:
nodeSelector:
disktype: ssd
containers:
- name: nginx
image: nginx
Now this Pod will only run on nodes with disktype=ssd. Very handy for database workloads that need fast disks.
View node labels:
kubectl get nodes --show-labels
Label Best Practices
Alright, here's the wisdom section. These will save you headaches down the road.
1. Be Consistent
Establish conventions, document them, and actually follow them:
app: <application-name>
environment: development | staging | production
team: <team-name>
version: v<major>.<minor>.<patch>
2. Use Hierarchical Names
For complex applications:
labels:
app.kubernetes.io/name: frontend
app.kubernetes.io/part-of: ecommerce-platform
3. Don't Over-Label
Add labels that serve a purpose:
- Selection (Services, Deployments)
- Organization (kubectl queries)
- Cost tracking
- Policy enforcement
Avoid labels you'll never query. Every label should earn its place — if you can't explain why it's there, it probably doesn't need to be.
4. Keep Values Simple
Labels have constraints:
- Max 63 characters
- Alphanumeric,
-,_,. - Must start and end with alphanumeric
Clean Up
kubectl delete pod web-prod-1 web-prod-2 web-dev api-prod ssd-pod 2>/dev/null
What's Next?
Awesome work! You can now organize, query, and manage resources like a Kubernetes power user. But here's a critical question — how do you know if your applications are actually healthy?
In the next tutorial, we'll dive into Health Checks — liveness, readiness, and startup probes that make sure your apps are running correctly and not just pretending to be fine. Let's go!