A namespace is a name-scoping boundary inside a cluster. Two Deployments can both be called api as long as they live in different namespaces, and RBAC, quotas and network policy can all be applied per namespace.
What a namespace is not is a security boundary on its own. Pods in different namespaces can reach each other over the network by default, and they share the same nodes and kernel.
kubectl api-resources --namespaced=true | head
kubectl api-resources --namespaced=false| Namespaced | Cluster-scoped |
|---|---|
| Pods, Deployments, StatefulSets, Jobs | Nodes |
| Services, Ingresses, EndpointSlices | PersistentVolumes |
| ConfigMaps, Secrets, ServiceAccounts | StorageClasses |
| PVCs, Roles, RoleBindings | ClusterRoles, ClusterRoleBindings |
| ResourceQuotas, LimitRanges, NetworkPolicies | CustomResourceDefinitions, Namespaces |
The PV/PVC split is the one that catches people: a PersistentVolumeClaim is namespaced but the PersistentVolume it binds is not. So a claim can only be used by Pods in its own namespace, while the underlying volume is a cluster resource.
Secrets being namespaced is the other frequent surprise. A registry credential created in default does nothing for a Pod in payments.
| Namespace | Contains |
|---|---|
default | Where objects go when you do not say otherwise |
kube-system | Control plane and cluster components. Do not deploy here |
kube-public | World-readable, rarely used |
kube-node-lease | Node heartbeat objects |
Avoid default for anything real. It has no quota, no network policy and no meaningful RBAC, and things accumulate there until nobody knows what is safe to delete.
kubectl create namespace payments
# or declaratively, which is what you want in Git
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Namespace
metadata:
name: payments
labels:
team: payments
environment: production
EOFkubectl get pods -n payments
kubectl get pods -A # every namespace
kubectl get namespacesSet a default so you stop typing -n:
kubectl config set-context --current --namespace=paymentsForgetting the namespace is the most common reason kubectl get pods reports "No resources found" when something is definitely running.
Label your namespaces. They are the handle for network policy selectors, quota reporting and cost allocation, and adding them later means reconstructing who owns what.
A Service's short name only resolves within its own namespace. Across namespaces you need the namespace segment:
payments-api # same namespace
payments-api.payments # from another namespace
payments-api.payments.svc.cluster.local # fully qualified
A call that works from one namespace and fails from another is nearly always this.
Where you genuinely want to alias a Service from elsewhere, an ExternalName Service does it without hardcoding the FQDN in application config:
apiVersion: v1
kind: Service
metadata:
name: payments-api
namespace: checkout
spec:
type: ExternalName
externalName: payments-api.payments.svc.cluster.localRBAC. A Role and RoleBinding grant permissions within one namespace, which is how you give a team full control of its own space and nothing else:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: payments-team
namespace: payments
subjects:
- kind: Group
name: payments-engineers
roleRef:
kind: ClusterRole
name: edit # built-in: read/write most things, no RBAC changes
apiGroup: rbac.authorization.k8s.ioNote the roleRef points at a ClusterRole while the binding itself is namespaced, that is the normal pattern for reusing the built-in view, edit and admin roles per namespace.
Resource quotas. Without one, any namespace can consume the whole cluster:
apiVersion: v1
kind: ResourceQuota
metadata:
name: payments-quota
namespace: payments
spec:
hard:
requests.cpu: "20"
requests.memory: 40Gi
pods: "50"Pair it with a LimitRange, because once a quota constrains a resource every Pod must specify it or be rejected.
Network policy. This is what turns a namespace into an actual boundary. By default all Pods can reach all Pods; a default-deny policy plus explicit allows changes that:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: payments
spec:
podSelector: {}
policyTypes: [Ingress]Network policies require a CNI that implements them. Calico, Cilium, and similar. On a CNI that does not, the policy is accepted and silently does nothing, which is a dangerous way to believe you are protected.
Common patterns, with their trade-offs:
payments, checkout). The usual choice. Clean RBAC and quota boundaries.dev, staging). Cheap, but a noisy dev workload shares nodes with staging and a cluster-wide incident hits both. Fine for non-production; separate clusters are better for production isolation.dev-alice). Good for ephemeral preview environments, needs automated cleanup.What does not work is one namespace for everything. You lose quotas, per-team RBAC and any meaningful network policy, and everything ends up in default.
kubectl delete namespace paymentsThis deletes everything inside it. Deployments, Services, Secrets, ConfigMaps, and PVCs. A PVC whose StorageClass has reclaimPolicy: Delete takes its data with it. Deleting a namespace is one of the most destructive single commands in Kubernetes.
Sometimes it hangs in Terminating indefinitely. The cause is virtually always a finalizer on a resource whose controller is gone, commonly a custom resource left behind after its operator was uninstalled:
kubectl get namespace payments -o json | jq '.status.conditions'"message": "Some resources are remaining: mycrds.example.com has 2 resource instances"
The correct fix is to remove the blocking resources or reinstall the operator so its finalizers can run:
kubectl get mycrds.example.com -n payments
kubectl patch mycrd my-instance -n payments -p '{"metadata":{"finalizers":null}}' --type=mergeForce-removing the namespace's own finalizer through the API is the widely-circulated trick, and it does work, but it orphans any resources that had not been cleaned up, including cloud load balancers and volumes that will keep billing. Treat it as a last resort and check for leftovers afterwards.
| Task | Command |
|---|---|
| Create | kubectl create namespace <name> |
| List | kubectl get namespaces |
| Work in one | kubectl get pods -n <name> |
| Across all | kubectl get pods -A |
| Set the default | kubectl config set-context --current --namespace=<name> |
| What is namespaced | kubectl api-resources --namespaced=true |
| Everything in one | kubectl get all -n <name> |
| Quota usage | kubectl describe resourcequota -n <name> |
| Why it will not delete | kubectl get ns <name> -o json | jq '.status.conditions' |
default. No quota, no policy, no ownership.Delete reclaim policy destroys the data.Terminating namespace is a finalizer, usually from an uninstalled operator.<MCQExercise exercise={{ id: 'k8s-ns-not-security', title: 'How isolated is a namespace?', description: 'A team puts an internal admin service in its own namespace, assuming other teams cannot reach it. Is that assumption correct on a default cluster?', options: [ { id: 'a', text: 'No. Pods in any namespace can reach it over the network unless a NetworkPolicy denies it' }, { id: 'b', text: 'Yes, namespaces are isolated network boundaries by default' }, { id: 'c', text: 'Yes, but only if the Service is type ClusterIP' }, { id: 'd', text: 'No, but only because DNS resolves across namespaces' }, ], correctId: 'a', explanation: 'Kubernetes networking is flat by default: every Pod can reach every other Pod and Service in the cluster, whatever namespace they are in. Namespaces scope names and give RBAC and quotas something to attach to, but they do not filter traffic. Network isolation requires NetworkPolicy resources and a CNI that enforces them.', wrongExplanations: { b: 'This is the most common and most dangerous misconception about namespaces. Nothing about creating one changes packet flow.', c: 'ClusterIP restricts reachability to inside the cluster, not to inside a namespace. Any Pod in the cluster can still connect.', d: 'DNS makes it convenient, but blocking DNS would not help. The Service IP is routable regardless of whether a name resolves to it.', }, }} />
<MCQExercise exercise={{ id: 'k8s-ns-terminating', title: 'A namespace that will not delete', description: 'A namespace has been Terminating for an hour. Its conditions report "Some resources are remaining: widgets.example.com has 2 resource instances". What is the correct first action?', options: [ { id: 'a', text: 'Remove the blocking custom resources or reinstall the operator, so the finalizers can complete properly' }, { id: 'b', text: 'Force-remove the namespace finalizer through the API to make it disappear immediately' }, { id: 'c', text: 'Restart the kube-controller-manager to clear the stuck deletion' }, { id: 'd', text: 'Delete the CustomResourceDefinition, which cascades to the namespace' }, ], correctId: 'a', explanation: 'A namespace waits until every resource inside it is genuinely cleaned up. Custom resources carry finalizers that their operator is supposed to run, releasing a cloud load balancer, detaching a volume, and if the operator has been removed nothing completes them. Clearing the blocking resources, or restoring the controller so it can finish properly, lets the deletion proceed without leaving anything orphaned.', wrongExplanations: { b: 'It does make the namespace vanish, which is why the trick circulates widely, but it abandons whatever those finalizers were meant to clean up, including cloud resources that keep billing invisibly.', c: 'The controller manager is working correctly; it is waiting deliberately. Restarting it changes nothing about the outstanding finalizers.', d: 'Deleting the CRD removes the type definition while its instances are still pending cleanup, which generally makes the situation harder to unpick rather than easier.', }, }} />
Only partly, and assuming otherwise is a common mistake. Kubernetes networking is flat by default, so a Pod in any namespace can reach a Service in any other. Namespaces do provide an RBAC boundary, so you can stop people managing resources elsewhere, but they do not stop workloads talking to each other. Real isolation needs NetworkPolicy resources enforced by a capable CNI such as Calico or Cilium, plus Pod Security Standards. For strong isolation between untrusted tenants, separate clusters remain the safer answer.
Include the namespace in the DNS name: service-name.namespace, or the fully qualified service-name.namespace.svc.cluster.local. The bare short name only resolves within the caller's own namespace because the cluster DNS search path appends that namespace first. This is why a request that works between two services in the same namespace fails the moment one of them moves.
Nodes, PersistentVolumes, StorageClasses, ClusterRoles, ClusterRoleBindings, CustomResourceDefinitions and namespaces themselves are all cluster-scoped. kubectl api-resources --namespaced=false lists them for your cluster. The distinction that surprises people most is that PersistentVolumeClaims are namespaced while the PersistentVolumes they bind are not, and that Secrets are namespaced, so a registry pull secret must exist in the same namespace as the Pod that needs it.
Something inside it has a finalizer that is not completing, almost always a custom resource whose operator has been uninstalled so nothing is left to run the cleanup. Check with kubectl get namespace <name> -o json | jq '.status.conditions', which names the remaining resource types. Fix it by deleting those resources or reinstalling the operator. Force-removing the namespace's finalizer is widely suggested and does work, but it orphans whatever the finalizers were protecting, including cloud load balancers and disks that carry on costing money.
Namespaces per environment are cheap and fine for development and staging, where sharing nodes and a control plane is acceptable. For production, separate clusters are safer: namespaces share nodes, a kernel and a control plane, so a resource-hungry workload or a cluster-wide upgrade affects everything at once. A common arrangement is one non-production cluster split by namespace and a dedicated production cluster, itself split by team or service.
Learn what a Kubernetes Pod is, what its containers share, why pod IPs change, how to write one, and how to read the statuses you see when debugging.
Write a Kubernetes Pod manifest with confidence. Walks through apiVersion, metadata, the container spec, ports and securityContext with a full example.
Understand Kubernetes pod phases and container states, restartPolicy, termination and eviction, and why CrashLoopBackOff is a symptom rather than the cause.