The three standard levels
| Level | Meaning | For |
|---|---|---|
| Privileged | No restrictions | System components such as CNI, CSI, monitoring agents |
| Baseline | Blocks known escalations: privileged, hostNetwork, hostPath, dangerous capabilities | The minimum for every app |
| Restricted | Adds: non-root required, drop ALL, seccomp, no privilege escalation | Ordinary apps; the target to aim for |
The old PodSecurityPolicy was removed in Kubernetes 1.25. The built-in replacement is Pod Security Admission (PSA), an integrated admission controller configured with namespace labels.
Three modes
| Label | On violation |
|---|---|
pod-security.kubernetes.io/enforce |
Reject the Pod |
pod-security.kubernetes.io/audit |
Allow it, record it in the audit log |
pod-security.kubernetes.io/warn |
Allow it, return a warning to the user |
apiVersion: v1
kind: Namespace
metadata:
name: app
labels:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/audit: restricted
This pattern blocks Baseline violations and warns about anything not yet Restricted, so teams can progress step by step.
kubectl label --overwrite ns app pod-security.kubernetes.io/enforce=restricted
kubectl label --dry-run=server --overwrite ns app pod-security.kubernetes.io/enforce=restricted
The second command is a trial: the server warns about running Pods that would violate the level, without changing anything.
flowchart LR
U["kubectl apply Pod"] --> API["API server"]
API --> PSA{"Pod Security Admission<br/>namespace labels"}
PSA -- "violates enforce" --> X["403 Forbidden"]
PSA -- "violates warn" --> W["Created + Warning"]
PSA -- "passes" --> OK["Pod created"]
Notes
- PSA checks Pods on creation; the Deployment is still created, but its ReplicaSet cannot create Pods. Find the error with
kubectl describe rsorkubectl get events. - You can pin a version:
pod-security.kubernetes.io/enforce-version: v1.31. - PSA has only three fixed levels. For custom rules (required registries, required labels) use Kyverno or Gatekeeper.
Traps: enabling enforce: restricted on a busy namespace without trying --dry-run=server; leaving namespaces unlabelled (the default is Privileged); labelling a whole app namespace privileged because one Pod needs it.