Kubernetes Namespace Management and Multi-Tenancy
Organize workloads with Kubernetes namespaces. Learn resource quotas, limit ranges, RBAC scoping, and network policies for multi-tenant clusters.
What you'll learn
- ✓How namespaces provide logical isolation in a cluster
- ✓Setting resource quotas to cap CPU and memory per namespace
- ✓Using LimitRanges to set default container limits
- ✓Scoping RBAC to namespaces for team-level access control
- ✓Network policies for namespace-level traffic isolation
Prerequisites
None — this post is self-contained.
A single Kubernetes cluster often runs workloads for multiple teams, environments, or customers. Namespaces provide the logical boundary that keeps them organized. Combined with resource quotas, RBAC, and network policies, namespaces give you soft multi-tenancy without the cost of running separate clusters.
What Namespaces Are
A namespace is a virtual partition within a cluster. Resources like pods, services, and deployments belong to exactly one namespace. Names must be unique within a namespace but can repeat across namespaces.
Kubernetes starts with four namespaces:
kubectl get namespaces
# NAME STATUS AGE
# default Active 30d
# kube-system Active 30d
# kube-public Active 30d
# kube-node-lease Active 30d
- default is where resources go when no namespace is specified
- kube-system contains cluster infrastructure (DNS, scheduler, controllers)
- kube-public is readable by all users, typically empty
- kube-node-lease holds node heartbeat objects
Creating Namespaces
apiVersion: v1
kind: Namespace
metadata:
name: team-backend
labels:
team: backend
env: production
Or imperatively:
kubectl create namespace team-backend
kubectl label namespace team-backend team=backend env=production
Labels on namespaces are important. They enable namespace-scoped network policies and make it easy to query resources across namespaces.
Namespace Naming Conventions
Adopt a consistent naming scheme. Common patterns:
- By team:
team-backend,team-frontend,team-data - By environment:
dev,staging,production - By team and environment:
backend-prod,backend-staging - By tenant:
customer-acme,customer-globex
Choose one pattern and enforce it. Inconsistent naming creates confusion as the cluster grows.
Resource Quotas
A ResourceQuota caps the total resources a namespace can consume. This prevents one team from monopolizing the cluster:
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-backend-quota
namespace: team-backend
spec:
hard:
requests.cpu: "20"
requests.memory: 40Gi
limits.cpu: "40"
limits.memory: 80Gi
pods: "100"
services: "20"
persistentvolumeclaims: "30"
This namespace can use at most 20 CPU cores of requests, 40 Gi of memory requests, and run at most 100 pods. Once the quota is reached, new pods are rejected until existing resources are freed.
Check quota usage:
kubectl describe resourcequota team-backend-quota -n team-backend
# Name: team-backend-quota
# Resource Used Hard
# -------- ---- ----
# limits.cpu 12 40
# limits.memory 24Gi 80Gi
# pods 35 100
# requests.cpu 6 20
# requests.memory 12Gi 40Gi
LimitRanges
A ResourceQuota caps the namespace total. A LimitRange sets defaults and constraints for individual containers:
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: team-backend
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 256Mi
defaultRequest:
cpu: 100m
memory: 128Mi
max:
cpu: "4"
memory: 4Gi
min:
cpu: 50m
memory: 64Mi
If a developer deploys a pod without specifying resource requests or limits, the LimitRange injects the defaults. If they specify values outside the min/max range, the API server rejects the pod.
This is critical when using ResourceQuotas. If a quota is active, Kubernetes requires every container to have resource requests and limits. Without a LimitRange providing defaults, pods without explicit limits are rejected.
Scoping RBAC to Namespaces
Kubernetes RBAC naturally scopes to namespaces through Roles and RoleBindings. A Role grants permissions within a single namespace:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
namespace: team-backend
rules:
- apiGroups: ["", "apps", "batch"]
resources: ["pods", "deployments", "services", "jobs", "configmaps"]
verbs: ["get", "list", "watch", "create", "update", "delete"]
- apiGroups: [""]
resources: ["pods/log", "pods/exec"]
verbs: ["get", "create"]
Bind it to a group:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: backend-developers
namespace: team-backend
subjects:
- kind: Group
name: backend-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: developer
apiGroup: rbac.authorization.k8s.io
The backend team can manage pods, deployments, and services in team-backend but has no access to other namespaces.
Network Policies for Namespace Isolation
By default, all pods in a Kubernetes cluster can communicate with all other pods, regardless of namespace. Network policies restrict this:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: team-backend
spec:
podSelector: {}
policyTypes:
- Ingress
ingress: []
This denies all incoming traffic to pods in team-backend. Then selectively allow traffic from within the same namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace
namespace: team-backend
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: team-backend
And allow traffic from a specific other namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-frontend
namespace: team-backend
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
team: frontend
ports:
- port: 8080
protocol: TCP
Network policies require a CNI plugin that supports them (Calico, Cilium, Weave Net). The default kubenet CNI does not enforce network policies.
Cross-Namespace Service Access
Services are accessible across namespaces using their fully qualified DNS name:
<service-name>.<namespace>.svc.cluster.local
For example, a frontend in team-frontend calling a backend API in team-backend:
env:
- name: API_URL
value: "http://api.team-backend.svc.cluster.local:8080"
This works even with network policies in place, as long as the policies explicitly allow the traffic.
Namespace Lifecycle
Delete a namespace to remove everything inside it:
kubectl delete namespace team-backend
This is a cascading delete. Every resource in the namespace, including pods, services, secrets, and PVCs, is removed. This is irreversible. Protect production namespaces with RBAC that restricts the delete verb on namespace resources to cluster administrators only.
Practical Recommendations
Create one namespace per team or per service boundary. Apply ResourceQuotas to every namespace to prevent resource monopolization. Add LimitRanges to provide sane defaults and prevent oversized containers. Scope RBAC with namespace-level Roles and RoleBindings. Apply a default-deny network policy to each namespace and explicitly allow required traffic. Label namespaces consistently so network policies and monitoring queries can select them easily.
Related articles
- Kubernetes Kubernetes cert-manager: Automate TLS Certificates
Set up cert-manager in Kubernetes to automate TLS certificate provisioning with Let's Encrypt. Covers Issuers, Certificates, Ingress integration.
- Kubernetes Kubernetes Cost Optimization: Right-Size Your Cluster
Reduce Kubernetes costs with right-sizing, autoscaling, spot instances, resource quotas, and monitoring. Practical strategies with real savings.
- Kubernetes Kubernetes Kustomize: Manage Configs Without Helm
Learn Kustomize for Kubernetes config management. Covers overlays, patches, generators, and multi-environment deployments without templating.
- Kubernetes Kubernetes Logging with Fluent Bit and Elasticsearch
Set up centralized Kubernetes logging with Fluent Bit, Elasticsearch, and Kibana. Covers DaemonSets, parsers, filters, and production tuning.