Chapter 8
Namespaces, Labels, Selectors & Organizing a Cluster
Before you read, guessWhat are the four built-in namespaces in Kubernetes and their primary purposes?
Take ten seconds and guess — even a wrong guess makes the answer stick. Tap to see where the chapter lands, or just read on.
Built-in namespaces: default (where things land unless told otherwise), kube-system (control plane components, as seen in Post 2), kube-public, and kube-node-lease.
So far, every Pod, Deployment, and Service you've created has lived in one shared space. That's fine when you're the only one using the cluster. A real cluster hosts several teams and several environments on the same hardware, at the same time. It needs three things: walls to keep things apart, tags to describe what things are, and a way to find things by tag. That's this whole post.
One cluster, many rooms
Every object you've created so far landed in the same place, called default. You never had to think about it, because you never needed more than one room. Now picture a real company cluster: team-a and team-b both want to run something called api. Or you want dev, staging, and prod copies of the same app on the same hardware, without them colliding or accidentally talking to each other's databases.
A namespace is Kubernetes' answer to this: a walled-off slice of one physical cluster, so object names only have to be unique inside that slice, not across the whole cluster. That means team-a's api Service and team-b's api Service can both exist without fighting over the name. Namespaces are also where two things you'll meet later get scoped: RBAC (rules about who is allowed to do what) and resource quotas (limits on how much CPU or memory a team can use). You'll cover both in a later post — for now, just know the namespace is the wall those rules get nailed to.
Every cluster ships with a few built-in namespaces, and you've already met one without realizing it. Back in Post 2, the Pods running the control plane weren't in default — they were in kube-system, a namespace where Kubernetes keeps its own internal machinery (API server, scheduler, controller manager, DNS) out of your way. The other built-ins are kube-public (readable by everyone, rarely used) and kube-node-lease (holds small "I'm still alive" heartbeat objects that nodes update). And default, where anything lands if you don't say otherwise.
Creating and using your own namespace is simple:
$ kubectl create namespace dev
namespace/dev created
$ kubectl get namespaces
NAME STATUS AGE
default Active 40d
kube-node-lease Active 40d
kube-public Active 40d
kube-system Active 40d
dev Active 4s
$ kubectl get pods -n dev
No resources found in dev namespace.
Notice the -n dev flag. Without it, kubectl get pods only shows you the default namespace (or whatever namespace your context currently points at) — it does not show everything everywhere. Typing -n dev on every single command gets old fast, so switch your default instead:
$ kubectl config set-context --current --namespace=dev
Context "minikube" modified.
This tells your current context "assume dev unless I say otherwise," so every following command quietly runs there until you change it again. This is a real time-saver once you're juggling several namespaces in one day.
kubectl get pods, see an empty list or "not found," and start debugging a problem that doesn't exist — you were just looking in the wrong room. Before you panic, check where you actually are: kubectl config view --minify | grep namespace. If a task doesn't name a namespace, don't guess — pass -n <namespace> explicitly, or use -A / --all-namespaces to see everything at once.department:sales or level:senior — plain information stuck to you, nothing more, it doesn't change how you do your job. Selectors are the instruction read out over the building intercom: "Everyone wearing a badge that says department:sales, report to the lobby." The intercom doesn't know anyone's name. It just reads badges and calls out whoever matches.Labels: the badge everything already wears
Here's where two earlier posts quietly click into place. A label is a key-value tag attached to an object, like env=prod or tier=frontend. A selector is a query that says "give me everything with this label." In Post 4, your Deployment found its own Pods using a label selector, not by tracking names one by one. In Post 5, your Service found its backend Pods the exact same way — a label selector, again, not names. At the time, those probably felt like two separate features to memorize. They're not. They're the same trick, used twice.
Labels can go on Pods, Deployments, Services, Nodes — basically anything in the Kubernetes API. By themselves, labels don't do anything. Attaching env=prod to a Pod doesn't change how it runs or what it can reach. A label is a sticky note, nothing more. All the power comes from something else reading those labels — a selector — which is exactly what your Deployments and Services have been quietly doing to you this whole time.
Let's attach some labels by hand and look at them:
$ kubectl label pod nginx env=prod tier=frontend
pod/nginx labeled
$ kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx 1/1 Running 0 2m env=prod,tier=frontend
Selectors: the intercom announcement
A selector is the query — the rule that decides which badges get paged. Kubernetes gives you two styles.
Equality-based is the simple, exact-match kind: "give me everything where this key equals this value." This is the style a Service's selector: app: nginx field uses in its YAML, and it's also what you type on the command line:
$ kubectl get pods -l env=prod
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 3m
Comma-separate multiple labels for AND, or exclude a value with !=.
Set-based is more flexible: "give me everything where this key's value is one of these options."
$ kubectl get pods -l 'environment in (prod,staging)'
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 3m
web-a1x 1/1 Running 0 9m
Set-based selectors also support notin and exists (matching any object that simply has the key, no matter its value). Deployments and ReplicaSets actually use this same set-based syntax internally in their YAML (matchLabels and matchExpressions) — you've been relying on it the whole time without seeing it.
nginx Pod from the examples above with env=staging instead of prod, then run kubectl get pods -l env=prod again — it should now come back empty, since the Pod no longer matches. Then try kubectl get pods -l 'env in (prod,staging)' and confirm it shows up again. This is exactly the mechanism a Service uses to instantly stop or start sending traffic to a Pod the moment its labels change.Annotations: metadata that isn't for finding things
One more term worth knowing so you don't mix it up with labels: an annotation. Annotations look identical to labels — they're also key-value pairs on an object — but they exist purely to store information, never to be searched or matched on. Things like a build number, a Git commit hash, or a note that some tool (an ingress controller, a CI/CD pipeline) needs to remember. Kubernetes itself never uses an annotation inside a selector, and neither should you. If you ever want to filter or group by a value, that value belongs in a label, not an annotation. Simple rule: labels are for identifying and grouping, annotations are for describing.
Key Takeaways
- A namespace is a walled-off slice of one physical cluster, used to separate teams, environments, or projects so object names don't collide and access/quotas can be scoped per namespace.
- Built-in namespaces:
default(where things land unless told otherwise),kube-system(control plane components, as seen in Post 2),kube-public, andkube-node-lease. kubectl config set-context --current --namespace=<ns>sets your default namespace for the current context so you stop typing-nconstantly.- A label is a key-value tag on any object — purely descriptive by itself, but the exact mechanism Deployments (Post 4) and Services (Post 5) already use internally to find the Pods they manage or route to.
- A selector is a query over labels: equality-based (
env=prod) for exact matches, set-based (environment in (prod,staging)) for matching against a list of options. - Annotations look like labels but are never used for selection — they're just a place to stash extra descriptive metadata for humans or tooling.
- Missing or wrong namespace is a top CKA time-sink — always confirm with
kubectl config view --minify | grep namespace, or be explicit with-n/-A.