Skip to content
Codeloom
Kubernetes

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.

·5 min read · By Codeloom
Intermediate 10 min read

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.