BlogKubernetes

Pod Security Standards: enforce restricted without breakage

Roll out the restricted profile namespace by namespace, find violators with audit mode first, and carve exceptions that expire.

Apr 28, 2026·12 min readAdvanced

PodSecurityPolicy is gone. Its replacement, Pod Security Admission, is built into the API server and enforces the three Pod Security Standards — privileged, baseline, and restricted — with a single label per namespace. The trick isn't turning it on; it's turning it on without breaking every workload that was quietly running as root.

Here's what enforcement looks like from the caller's side — a pod that violates restricted is refused at admission, before a single container starts:

bash — kubectl apply (enforced)live
kubectl apply -f pod.yaml -n payments
namespace payments: pod-security.kubernetes.io/enforce=restricted
 
Error from server (Forbidden): error when creating "pod.yaml":
pods "api" is forbidden: violates PodSecurity "restricted:latest":
allowPrivilegeEscalation != false (container "api")
runAsNonRoot != true (pod or container "api")
seccompProfile type unset (pod or container "api")
 
fix the securityContext and re-apply — nothing was scheduled

Three profiles, one decision

baseline blocks the obviously dangerous — host namespaces, privileged containers, hostPath volumes. restricted goes further: non-root, no privilege escalation, a seccomp profile, and dropped capabilities. Most teams should target restricted for application namespaces and reserve baseline for the few workloads that genuinely need more.

What restricted demands
Rejected
runAsRoot (or unset)
allowPrivilegeEscalation: true
no seccompProfile
CAP_NET_RAW and friends
hostPath / host namespaces
Required
runAsNonRoot: true
allowPrivilegeEscalation: false
seccompProfile: RuntimeDefault
capabilities.drop: ["ALL"]
projected / emptyDir volumes

Start in audit mode

Never jump straight to enforce. Label the namespace with audit and warn first — the API server records violations to the audit log and returns a warning to kubectl, but still admits the pod. You get a complete list of what would break with zero downtime.

namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: payments
labels:
# observe only — nothing is blocked yet
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted

Deploy as usual for a sprint, then harvest the violations straight from the audit log and fix each workload's securityContext.

find-violators.sh
# pull restricted violations out of the audit log
grep 'pod-security.kubernetes.io/audit-violations' audit.log \
| jq -r '.objectRef.namespace + "/" + .objectRef.name'

Promote to enforce

Once the warnings are quiet, flip enforce on. Keep warn and audit pinned to latest so you catch regressions when a new Kubernetes release tightens the standard.

namespace.yaml
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/warn: restricted
Exceptions should expire
Some namespaces (ingress controllers, CNI, storage) legitimately need baseline. Grant them explicitly and put a review date on the exception — a permanent exception is just a hole with paperwork.

Where this goes next

PSA enforces pod-level posture but can't express org policy like 'images must come from our registry' or 'every pod needs a cost-center label'. That's where Kyverno or OPA Gatekeeper pick up. Pair PSA for the baseline with a policy engine for the rules that are specific to you.

Go deeper in a courseKubernetes administrationRBAC, admission control, and cluster hardening in depth.View course

Related posts