Intro
Kubernetes Namespace capacity planning answers a simple question: how much CPU, memory, and object count can a namespace consume before it risks cluster stability or starves other tenants? Without deliberate planning, a single namespace can exhaust node capacity, trigger evictions, or silently throttle workloads. This guide gives developers, DevOps consultants, and technical startup teams a repeatable, verifiable process for sizing namespaces, enforcing quotas, and scaling safely.
We focus on four primitives: namespace sizing, resource quotas, limit ranges, and scaling workflows. Each section moves from observation to intervention to verification. You will learn which commands to run, what output to expect, and how to recover when a constraint is violated. The workflow assumes a working Kubernetes cluster (v1.24 or later for stable quota and limit range behavior) and kubectl configured with appropriate permissions.
Operational safety is central: observe before changing, limit the blast radius, use placeholders instead of secrets in examples, verify the result, and document recovery paths. All examples are runnable on a local cluster such as kind or minikube.
Version and Environment Inventory
Capacity planning decisions depend on the Kubernetes version and the resource model in use. Start by collecting a read-only inventory of the cluster and the target namespace. This establishes the blast radius before any change.
Verifying the Kubernetes Version
kubectl version --short
Expected output resembles:
Client Version: v1.27.3
Kustomize Version: v5.0.1
Server Version: v1.27.1
Resource Quota behavior is stable across releases, but Limit Range defaults have evolved. For example, the ability to set defaultRequest and defaultLimit at the container level has been available since early versions, but verify your cluster's behavior with kubectl explain:
kubectl explain limitrange.spec.limits
If the field defaultRequest is present, your cluster supports container default requests. If not, you may need to upgrade or use a mutating admission webhook.
Inspecting Namespace State
Before touching anything, capture the current state of the namespace and its resource consumption:
kubectl get namespace <namespace> -o yaml
kubectl get resourcequota,limitrange -n <namespace>
kubectl top pod -n <namespace> --containers
kubectl top requires the metrics-server. If it is missing, install it via:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
Typical output for kubectl top pod:
POD CPU(cores) MEMORY(bytes)
web-5b8f7c9d6-abcde 12m 180Mi
api-7d4f8b9c2-fghij 45m 512Mi
Record these baselines. They determine whether a planned quota is loose or tight.
Identifying Workload Topology
List deployments, statefulsets, and daemonsets in the namespace to understand scaling behavior:
kubectl get deploy,sts,ds -n <namespace> -o wide
Focus on resource requests and limits per workload:
kubectl get deploy -n <namespace> -o=jsonpath='{range .items[*]}{.metadata.name}{" requested="}{.spec.template.spec.containers[*].resources.requests}{" limited="}{.spec.template.spec.containers[*].resources.limits}{"\n"}{end}'
If many workloads omit requests and limits, namespace capacity planning is impossible. A Limit Range can enforce defaults, which we cover next.
Safe Configuration Path
Applying capacity controls without incident requires a layered approach: first a Limit Range to set sane defaults for new pods, then a Resource Quota to cap aggregate consumption, then verification under realistic traffic. The smallest justified change is a single Limit Range applied to a test namespace.
Step 1: Create a Test Namespace
Always validate in an isolated namespace:
kubectl create namespace capacity-test
Step 2: Apply a Limit Range
A Limit Range enforces default requests and limits for containers that do not explicitly set them. It also constrains min/max values for any pod admitted to the namespace.
Save as limit-range.yaml:
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
spec:
limits:
- type: Container
max:
cpu: "1"
memory: "1Gi"
min:
cpu: "50m"
memory: "64Mi"
default:
cpu: "250m"
memory: "256Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
Apply and verify:
kubectl apply -f limit-range.yaml -n capacity-test
kubectl get limitrange -n capacity-test default-limits -o yaml
Observe the spec is echoed with defaults. Now create a pod without explicit resources:
apiVersion: v1
kind: Pod
metadata:
name: test-pod-defaults
spec:
containers:
- name: nginx
image: nginx:1.25
After applying, inspect the pod's effective resources:
kubectl get pod test-pod-defaults -n capacity-test -o jsonpath='{.spec.containers[0].resources}'
Expected output:
{"limits":{"cpu":"250m","memory":"256Mi"},"requests":{"cpu":"100m","memory":"128Mi"}}
The Limit Range injected safe defaults. This prevents a pod from being scheduled without resources, which could otherwise lead to unpredictable eviction.
Step 3: Apply a Resource Quota
A Resource Quota caps the total requests, limits, and object counts within the namespace. Save as resource-quota.yaml:
apiVersion: v1
kind: ResourceQuota
metadata:
name: namespace-quota
spec:
hard:
requests.cpu: "4"
requests.memory: "8Gi"
limits.cpu: "6"
limits.memory: "12Gi"
persistentvolumeclaims: "10"
pods: "20"
services: "5"
Apply and inspect current usage:
kubectl apply -f resource-quota.yaml -n capacity-test
kubectl get resourcequota -n capacity-test namespace-quota -o yaml
The status.hard and status.used fields show quota versus current consumption. Initially, used will reflect the pod created earlier:
status:
hard:
limits.cpu: "6"
limits.memory: 12Gi
persistentvolumeclaims: "10"
pods: "20"
requests.cpu: "4"
requests.memory: 8Gi
services: "5"
used:
limits.cpu: 250m
limits.memory: 256Mi
pods: "1"
requests.cpu: 100m
requests.memory: 128Mi
This shows one pod consuming 100m CPU requests and 128Mi memory requests, leaving plenty of headroom.
Step 4: Validate Quota Enforcement
Deploy a workload that attempts to exceed the quota. For example, create a deployment with 10 replicas, each requesting 1 CPU:
kubectl create deployment quota-breaker --image=nginx --replicas=10 -n capacity-test
kubectl set resources deployment quota-breaker -n capacity-test --requests=cpu=1,memory=1Gi --limits=cpu=1,memory=1Gi
kubectl rollout status deployment/quota-breaker -n capacity-test
The rollout will fail with events like:
Error creating: pods "quota-breaker-..." is forbidden: exceeded quota: namespace-quota, requested: requests.cpu=10, used: requests.cpu=100m, limited: requests.cpu=4
This proves the quota is working. To recover, scale down the deployment:
kubectl scale deployment quota-breaker --replicas=1 -n capacity-test
Then confirm quota usage returns within limits:
kubectl get resourcequota -n capacity-test namespace-quota
Verification and Diagnostics
Verification for capacity planning involves two layers: (1) confirming that controls are active, and (2) measuring actual consumption against planned headroom. This section provides commands and expected outputs for both.
Verifying Resource Quota and Limit Range Status
Run a combined check:
kubectl get resourcequota,limitrange -n capacity-test
Expected output:
NAME AGE
resourcequota/namespace-quota 10m
NAME CREATED AT
limitrange/default-limits 2023-10-01T12:00:00Z
For detailed quota usage:
kubectl describe resourcequota namespace-quota -n capacity-test
Output includes Used vs Hard for each constrained resource. Observe whether any resource is near its limit (e.g., >80%). If so, you must either increase the quota or scale down workloads.
Measuring Actual Consumption
Use kubectl top for live pod metrics:
kubectl top pods -n capacity-test --sort-by=cpu
Example:
NAME CPU(cores) MEMORY(bytes)
quota-breaker-5b8f7c9d6-abcde 120m 512Mi
test-pod-defaults 2m 45Mi
Compare these values to the requested amounts. A pod that requests 1 CPU but uses 120m is over-provisioned and inflates quota usage unnecessarily. Right-size requests based on observed peaks, not worst-case. A common practice is to set requests at P95 observed usage and limits at 2x requests.
Diagnosing Scheduling Failures
If pods remain Pending, check events:
kubectl describe pod <pending-pod> -n capacity-test
Look for messages such as:
Warning FailedScheduling 28s default-scheduler 0/3 nodes are available: 3 Insufficient cpu, 5 Insufficient memory.
This indicates node-level capacity constraints, not namespace quota. Verify node allocatable resources:
kubectl describe nodes | grep -A 5 "Allocatable"
If the namespace quota is the blocker, the event will mention "exceeded quota" as shown earlier.
Diagnostic Workflow for Capacity Incidents
- Identify the failing pod or deployment.
- Check
kubectl describe podfor events (quota, limit range, or node resources). - Check namespace quota usage:
kubectl describe resourcequota. - Check node capacity:
kubectl describe nodes. - Compare actual usage (
kubectl top) with requests and limits.
For crash loops unrelated to quota, use:
kubectl logs <pod-name> -n capacity-test --previous
Failure Modes and Recovery
Capacity misconfiguration leads to predictable failure modes. For each, we provide the signal, the likely cause, and the recovery procedure.
Failure 1: Pods Stuck in Pending with "exceeded quota"
Signal: Event in kubectl describe pod says forbidden: exceeded quota.
Cause: The namespace's aggregate resource requests would exceed the Resource Quota hard limit.
Recovery options:
- Reduce replicas:
kubectl scale deployment <name> --replicas=N - Lower resource requests for the workload (edit deployment YAML).
- Increase the namespace quota if team has capacity:
kubectl edit resourcequota namespace-quota -n <namespace>and raiserequests.cpuorrequests.memory.
Verification: Monitor kubectl get resourcequota until used is below hard.
Failure 2: Pod Creation Rejected by Limit Range
Signal: Pod remains uncreated, event says Forbidden: minimum cpu usage per Container is 50m or similar.
Cause: The pod's request is below the Limit Range minimum or above maximum.
Recovery: Adjust the pod's container resources to within min/max, or modify the Limit Range if the limits were set too strictly. Then re-apply the pod manifest.
Failure 3: Node-Level Eviction Due to Overcommit
Signal: Pods are evicted with reason Evicted in kubectl get pods, and node MemoryPressure or DiskPressure condition is True.
Cause: The sum of pod requests across namespaces exceeds node allocatable. Kubernetes evicts best-effort and burstable pods first.
Recovery:
- Add more node capacity (scale cluster).
- Set meaningful requests on all pods (Limit Range helps).
- Reduce overcommit by increasing requests, not limits.
- Use priority classes to protect critical workloads.
Verification: Check kubectl get events --field-selector reason=Evicted and node conditions.
Failure 4: PersistentVolumeClaim Quota Exhaustion
Signal: PVC creation fails with exceeded quota: persistentvolumeclaims.
Cause: The namespace PVC count hit the Resource Quota limit.
Recovery: Delete unused PVCs (after backing up) or increase the quota if storage class capacity allows. Before deleting, ensure no running pods reference the PVC.
Recovery Playbook Summary
| Failure | Signal | Immediate Action | Long-Term Fix |
|---|---|---|---|
| Quota exceeded | Pod Pending, events | Scale down or raise quota | Right-size requests, forecast usage |
| Limit Range violation | Pod rejected | Adjust pod resources | Design policies around real needs |
| Node eviction | Evicted pods, node pressure | Add nodes or reduce load | Set requests and limits, use priorities |
| PVC quota exhausted | PVC pending | Delete stale PVCs | Implement storage lifecycle |
Operations Checklist
The following checklist turns capacity planning into a routine, auditable process. Run through it before and after any significant change.
Pre-change Checklist
- Capture namespace state:
kubectl get resourcequota,limitrange -n <namespace> -o yaml > before-state.yaml
- Record current usage:
kubectl top pods -n <namespace> --containers > before-usage.txt
Use a script to compare requests vs actual usage (example using bash and awk):
- Identify workloads that are over-provisioned or under-provisioned:
kubectl top pods -n <namespace> --no-headers | awk '{print $1, $2, $3}'
# Compare with kubectl get pods -o jsonpath for requests.
Already saved in before-state.yaml.
- Back up resource quotas and limit ranges:
- Document the expected outcome in a change ticket (e.g., reduce namespace CPU requests by 20%).
Change Execution Checklist
- Apply one manifest at a time. Verify after each.
- Use
kubectl diff -f <manifest>to preview changes before applying. - For quota increases, use
kubectl patchto modify only the specific field:
kubectl patch resourcequota namespace-quota -n <namespace> --type='json' -p='[{"op": "replace", "path": "/spec/hard/requests.cpu", "value": "8"}]'
- After apply, run
kubectl rollout statusfor affected deployments.
Post-change Verification Checklist
- Confirm quota updated:
kubectl get resourcequota -n <namespace> -o yaml. - Check for new events:
kubectl get events -n <namespace> --sort-by=.lastTimestamp | tail -20. - Monitor pod scheduling and health for at least 10 minutes.
- Compare actual usage metrics to predictions; adjust requests if needed.
- Document the final state in a revision-controlled file (e.g., GitOps).
Local Testing Before Production
Always test capacity changes in a controlled namespace first. For example, to simulate high load:
kubectl run load-generator --image=busybox --restart=Never -- sh -c "while true; do wget -q -O- http://web-service; sleep 0.01; done"
Then observe whether pods maintain performance and whether quota is adequate. Use kubectl port-forward to expose a service locally and run load tests before adding an ingress or load balancer.
Conclusion
Kubernetes Namespace capacity planning is not a one-time calculation; it is a continuous loop of observe, enforce, verify, and recover. By combining Limit Ranges for sane defaults, Resource Quotas for tenant isolation, and a disciplined diagnostic workflow, operators can prevent noisy-neighbor incidents and avoid cluster-wide instability.
Start small: create a test namespace, apply a Limit Range, then a Resource Quota. Observe how pods receive defaults and how quota violations are reported. Use the commands in this guide as a reference, but always validate against your cluster's version and your team's workload patterns.
Next steps:
- Instrument your namespaces with Prometheus and Grafana to visualize quota usage over time.
- Integrate quota checks into your CI/CD pipeline: before deploying, run
kubectl auth can-i create pods --namespace <ns>and a dry-run apply. - Adopt GitOps for capacity changes: store quota and limit range YAML in version control, and use tools like Argo CD or Flux.
Reliable capacity planning makes failure visible, protects sensitive values, limits changes to the intended resource, and defines recovery verification before an incident forces the decision. With the practical examples in this article, you can confidently size, limit, and scale Kubernetes namespaces.