Namespaces, Labels and Selectors
In the two previous articles, both Deployment and Service "found pods by label" without us explaining it carefully. This article pays that debt: labels and selectors are the tagging and selection mechanism running throughout Kubernetes — understanding it is understanding how objects bind to each other. Alongside comes namespaces, how to split the cluster into separate compartments.
Namespaces: splitting the cluster into compartments
A cluster usually serves many teams, many environments, many applications. Dumping everything in one place is chaos. A namespace is a "virtual folder" that groups resources, allowing name isolation and applying separate quotas/permissions.
kubectl get namespaces
NAME STATUS AGE
default Active 11m
kube-node-lease Active 11m
kube-public Active 11m
kube-system Active 11m
Every cluster has a few built-in namespaces: default (where your resources land if you don't specify), kube-system (system components — remember in Article 1 we inspected the control plane pods here), kube-public, kube-node-lease. Create a new namespace:
kubectl create namespace dev
Then deploy into it with the -n flag:
kubectl create deployment api --image=nginx:alpine -n dev
kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
api-6676db897b-nlm78 1/1 Running 0 10s
The core point: resources in different namespaces are name-isolated from each other. You can have a Deployment web in default and another web in dev — they don't collide. kubectl get pods (without -n) only shows the current namespace; to see across all, use --all-namespaces (or -A):
kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS AGE
default web-5687994c96-682pg 1/1 Running 5m2s
dev api-6676db897b-nlm78 1/1 Running 10s
Some resources live inside a namespace (Pod, Deployment, Service, ConfigMap...), others are cluster-scoped, belonging to no namespace (Node, Namespace, PersistentVolume). If you switch namespaces often, a tool like
kubenssaves your fingers; or set a default:kubectl config set-context --current --namespace=dev.
Note: a namespace is not a network security boundary by default — a pod in one namespace can still call a pod in another via DNS (web.default). To block that you need a NetworkPolicy (out of scope for this fundamentals series).
Labels: free-form tags for resources
A label is a key=value pair you attach to an object to classify it in ways meaningful to you: app=web, tier=frontend, env=production, version=v2... Labels do nothing on their own — the power comes from selecting by them.
View the labels on the web pods:
kubectl get pods -l app=web --show-labels
NAME READY STATUS LABELS
web-5687994c96-682pg 1/1 Running app=web,pod-template-hash=5687994c96
web-5687994c96-qmkr5 1/1 Running app=web,pod-template-hash=5687994c96
web-5687994c96-spvjm 1/1 Running app=web,pod-template-hash=5687994c96
Two labels: app=web (we set it in the Article 4 manifest) and pod-template-hash=... (added automatically by the Deployment). The second one is worth noticing — it's exactly how the ReplicaSet distinguishes "pods of which version" during a rolling update (Article 4): each ReplicaSet has its own hash, stamped onto the pods it creates. The system uses labels to manage itself.
Attach/change a label manually:
kubectl label pod web-5687994c96-682pg tier=frontend
pod/web-5687994c96-682pg labeled
Selectors: filtering by label
A selector is a query that filters objects by label. This is what Deployment and Service have been quietly using. Filter with the -l flag:
kubectl get pods -l 'app=web,tier=frontend' # matches: 1 (the pod just labeled)
kubectl get pods -l app=web # matches: 3 (all web pods)
A comma means AND — app=web,tier=frontend selects pods with both labels. Besides equality, selectors also support other operations: env in (prod,staging), tier!=backend, or just the existence of a key (tier).
This is exactly the mechanism we've met over the past few articles:
Service "web" ──selector app=web──► every pod with label app=web
Deployment "web" ──selector app=web──► manages pods with label app=web
Because it's dynamic selection, it automatically covers new pods: any pod stamped with the right label automatically falls within reach of the Service/Deployment, no re-declaration needed. That's why self-healing and scaling (Article 4) "fit snugly" with the Service's load balancing (Article 5) — everything speaks the same label language.
A practical tip: recommended standard labels
Kubernetes recommends a common label set (app.kubernetes.io/name, app.kubernetes.io/version, app.kubernetes.io/part-of...) so that tools and people understand resources uniformly. Not mandatory, but following it from the start makes large clusters far easier to manage — consistently labeling env, app, version makes filtering logs, debugging, and applying policies later much cleaner.
Wrap-up
A namespace splits the cluster into name-isolated compartments (every cluster ships with default, kube-system...); use -n/--all-namespaces to work across all — but remember a namespace doesn't block networking by itself. A label is a free-form key=value tag attached to objects (including system labels added automatically, like pod-template-hash), and a selector is a query that filters by label (-l app=web, supporting AND/in/existence). This is the foundational "glue": Deployment and Service find pods dynamically via selectors, which is why all the scaling/self-healing/load-balancing mechanisms fit together.
The application runs and is reachable, but the configuration (environment variables, passwords) is still baked into the image. Article 7: ConfigMap and Secret — separating configuration from the image so the same image runs in every environment.