Kubernetes Pod Security Admission lets a namespace reject Pods that do not meet the built-in Pod Security Standards. Enforcing a standard is useful when application teams can still deploy ordinary workloads, but privileged Pods, host access, and missing hardening fields should stop at the API server.
The admission controller reads labels on the Namespace object. The enforce mode blocks non-compliant Pods, while warn returns user-facing warnings and audit records audit annotations without changing the enforcement decision.
Pod Security Admission is non-mutating, so workloads must set the required securityContext fields themselves. Keep namespace label permissions with trusted administrators, and test workload manifests with a server-side dry run before moving a production namespace from warning-only review into enforcement.
$ kubectl create namespace team-a namespace/team-a created
Use the target workload namespace instead of team-a when enforcing policy on an existing application namespace.
Related: How to create a Kubernetes namespace
$ kubectl label namespace team-a --overwrite --dry-run=server \ pod-security.kubernetes.io/enforce=restricted \ pod-security.kubernetes.io/enforce-version=latest \ pod-security.kubernetes.io/audit=restricted \ pod-security.kubernetes.io/audit-version=latest \ pod-security.kubernetes.io/warn=restricted \ pod-security.kubernetes.io/warn-version=latest namespace/team-a labeled (server dry run)
--dry-run=server validates the namespace label update without saving it. Existing Pods that violate a new enforce policy return warnings during this check.
$ kubectl label namespace team-a --overwrite \ pod-security.kubernetes.io/enforce=restricted \ pod-security.kubernetes.io/enforce-version=latest \ pod-security.kubernetes.io/audit=restricted \ pod-security.kubernetes.io/audit-version=latest \ pod-security.kubernetes.io/warn=restricted \ pod-security.kubernetes.io/warn-version=latest namespace/team-a labeled
enforce rejects non-compliant Pods. warn and audit keep client warnings and audit records at the same standard so workload owners can see the same failures before or during rollout.
Use enforce-version=latest to follow the cluster's current Pod Security rules, or pin a minor version such as v1.36 when policy changes should happen separately from Kubernetes upgrades.
$ kubectl describe namespace team-a
Name: team-a
Labels: kubernetes.io/metadata.name=team-a
pod-security.kubernetes.io/audit=restricted
pod-security.kubernetes.io/audit-version=latest
pod-security.kubernetes.io/enforce=restricted
pod-security.kubernetes.io/enforce-version=latest
pod-security.kubernetes.io/warn=restricted
pod-security.kubernetes.io/warn-version=latest
Annotations: <none>
Status: Active
No resource quota.
No LimitRange resource.
apiVersion: v1 kind: Pod metadata: name: privileged-test spec: containers: - name: debug image: busybox:1.36 command: ["sleep", "1d"] securityContext: privileged: true
The restricted profile rejects privileged containers and also expects explicit hardening fields such as non-root execution, dropped capabilities, privilege-escalation denial, and a seccomp profile.
$ kubectl apply --dry-run=server -f restricted-violation-pod.yaml --namespace team-a Error from server (Forbidden): error when creating "restricted-violation-pod.yaml": pods "privileged-test" is forbidden: violates PodSecurity "restricted:latest": privileged (container "debug" must not set securityContext.privileged=true), allowPrivilegeEscalation != false (container "debug" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "debug" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "debug" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "debug" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
The Forbidden response proves the namespace enforce label is active before the violating Pod is stored.
apiVersion: v1 kind: Pod metadata: name: restricted-ok spec: securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault containers: - name: app image: busybox:1.36 command: ["sleep", "1d"] securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL runAsUser: 1000 runAsGroup: 1000
The Pod-level seccompProfile and non-root requirement apply to the container, while the container-level block denies privilege escalation and drops Linux capabilities.
Tool: Kubernetes Pod Security Context Checker
$ kubectl apply -f restricted-ok-pod.yaml --namespace team-a pod/restricted-ok created
$ kubectl wait --for=condition=Ready pod/restricted-ok --namespace team-a --timeout=120s pod/restricted-ok condition met
$ kubectl get pod restricted-ok --namespace team-a --output=wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES restricted-ok 1/1 Running 0 1s 10.244.0.6 worker-1 <none> <none>
A Running Pod proves the namespace still admits workloads that meet the selected Pod Security Standard.
$ kubectl delete pod restricted-ok --namespace team-a pod "restricted-ok" deleted from team-a namespace
$ rm restricted-violation-pod.yaml restricted-ok-pod.yaml
The namespace keeps its Pod Security labels after the proof Pods and local test files are removed.