Namespace resource policy in Kubernetes keeps CPU and memory expectations close to the workloads that enter the namespace. A LimitRange supplies default requests and limits for Pods that omit them, and it rejects objects that sit outside the allowed bounds.
LimitRange admission runs when an object is created or updated in the namespace. Defaults are injected before constraint checks, so a Pod with no resource block can receive policy values, while a Pod with limits above the maximum is refused by the API server.
Apply the policy before tenant workloads are created, because existing Pods are not rewritten just because a namespace policy changes. Choose CPU and memory values from workload measurements and platform policy, then use a server-side dry run to test the rejection path before handoff.
$ kubectl create namespace team-a namespace/team-a created
Use an existing namespace when the tenant namespace already exists. A LimitRange affects new and updated API objects in that namespace, not Pods that were already admitted.
Related: How to create a Kubernetes namespace
apiVersion: v1 kind: LimitRange metadata: name: team-a-container-defaults spec: limits: - type: Container default: cpu: 500m memory: 256Mi defaultRequest: cpu: 100m memory: 128Mi min: cpu: 50m memory: 64Mi max: cpu: "1" memory: 512Mi
defaultRequest values become missing container requests, and default values become missing container limits. min and max values bound accepted container resource settings.
$ kubectl apply -f team-a-limits.yaml --namespace team-a limitrange/team-a-container-defaults created
$ kubectl describe limitrange team-a-container-defaults --namespace team-a Name: team-a-container-defaults Namespace: team-a Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio ---- -------- --- --- --------------- ------------- ----------------------- Container cpu 50m 1 100m 500m - Container memory 64Mi 512Mi 128Mi 256Mi -
$ kubectl run defaulted --image=nginx --restart=Never --namespace team-a pod/defaulted created
The Pod does not need to reach Running for this admission check. The API server stores the resource fields before the kubelet pulls the image.
$ kubectl get pod defaulted -n team-a --output=jsonpath='{.spec.containers[0].resources}'
{"limits":{"cpu":"500m","memory":"256Mi"},"requests":{"cpu":"100m","memory":"128Mi"}}
The output shows the default request and limit values injected into the container specification.
apiVersion: v1 kind: Pod metadata: name: too-large spec: containers: - name: too-large image: nginx resources: requests: cpu: 100m memory: 128Mi limits: cpu: "2" memory: 256Mi
$ kubectl apply --dry-run=server -f too-large-pod.yaml --namespace team-a Error from server (Forbidden): error when creating "too-large-pod.yaml": pods "too-large" is forbidden: maximum cpu usage per Container is 1, but limit is 2
--dry-run=server sends the object through API validation and admission without saving it.
$ kubectl delete pod defaulted --namespace team-a --ignore-not-found pod "defaulted" deleted from team-a namespace
$ rm too-large-pod.yaml
Keep team-a-limits.yaml in source control or the platform configuration repo so the namespace policy can be reviewed and reapplied.