If you learned Kubernetes security a few years ago, half of what you learned about pod hardening is now obsolete. PodSecurityPolicy — the old admission controller that enforced what a pod could do — was deprecated in v1.21 and removed entirely in v1.25. In its place, Kubernetes ships Pod Security Admission, a built-in controller that enforces the three Pod Security Standards. This is not a cosmetic rename: PSP was cluster-scoped, order-dependent, and notoriously hard to get right, while the new model is namespace-scoped, label-driven, and far simpler to reason about. If your clusters are on any supported version in 2026, PSP is not an option and you should be running Pod Security Admission or an equivalent policy engine. This guide explains the standards and how to roll them out safely.
The three standards
Pod Security Standards define three cumulative profiles, from most permissive to most locked-down:
- Privileged — unrestricted. Allows everything, including host access and privileged containers. Intended for trusted, infrastructure-level workloads (CNI agents, storage drivers) and nothing else.
- Baseline — blocks known privilege escalations while staying broadly compatible. No
privileged: true, no host namespaces, nohostPathvolumes, no adding dangerous capabilities. Most general application workloads pass baseline with minor changes. - Restricted — the hardened target. Everything baseline forbids, plus: must run as non-root, must drop
ALLcapabilities, must setseccompProfiletoRuntimeDefault, and must disallow privilege escalation.
The three modes
Pod Security Admission applies a standard to a namespace through labels, and each standard can run in one of three modes simultaneously:
- enforce — rejects pods that violate the profile.
- audit — allows the pod but records a violation in the audit log.
- warn — allows the pod but returns a warning to the user who created it.
This trio is the key to a safe rollout: you can audit and warn at restricted while only enforce-ing at baseline, giving you a live report of what would break before you actually break it.
Applying it to a namespace
Everything is driven by labels on the Namespace object. You can pin the version so a cluster upgrade does not silently tighten the rules underneath you:
apiVersion: v1
kind: Namespace
metadata:
name: payments
labels:
# Hard-fail anything below baseline
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: v1.31
# But show me what restricted would reject, without blocking it yet
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
Apply it, then create a deliberately non-compliant pod and watch the warning appear:
kubectl label namespace payments \
pod-security.kubernetes.io/warn=restricted --overwrite
# A pod with no securityContext will now trigger restricted warnings
kubectl run test --image=nginx -n payments
# Warning: would violate PodSecurity "restricted:v1.31": allowPrivilegeEscalation != false ...
What a restricted-compliant pod looks like
The gap between "runs today" and "passes restricted" is almost always the securityContext. Here is a spec that satisfies the restricted profile:
apiVersion: v1
kind: Pod
metadata:
name: hardened
namespace: payments
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myregistry.example.com/app@sha256:abc123...
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
Note that seccompProfile: RuntimeDefault can be set at the pod level, but capabilities, allowPrivilegeEscalation, and readOnlyRootFilesystem are per-container.
A migration path that will not page you at 2 a.m.
- Inventory in audit mode cluster-wide. Label namespaces with
audit: restrictedandwarn: restrictedand leave enforce off. Collect the audit annotations for a week to see exactly which workloads fail and why. - Fix the easy wins. Most failures are missing
securityContextfields, not genuine needs for privilege. Add the non-root and capability-drop settings. - Enforce baseline first. It is a low bar that catches the truly dangerous pods and rarely breaks legitimate apps.
- Promote namespaces to enforce restricted one at a time. Start with new or low-risk namespaces; leave the messy legacy namespace for last.
- Exempt infrastructure deliberately. Workloads that legitimately need privilege (CNI, CSI drivers) go in dedicated
privilegednamespaces, never mixed with application workloads.
Where Pod Security Admission stops
Pod Security Admission is intentionally limited: it enforces the three fixed standards and nothing else. It cannot say "images must come from our registry," "every pod must carry a cost-center label," or "no latest tags." For custom rules you layer a policy engine — Kyverno, OPA Gatekeeper, or the built-in ValidatingAdmissionPolicy — on top of the baseline that Pod Security Admission provides. The two are complementary, not competing.
Profile comparison
| Restriction | Baseline | Restricted |
|---|---|---|
privileged: true | Blocked | Blocked |
Host namespaces / hostPath | Blocked | Blocked |
| Run as non-root | Allowed as root | Required |
| Drop ALL capabilities | Not required | Required |
seccompProfile: RuntimeDefault | Not required | Required |
allowPrivilegeEscalation: false | Not required | Required |
How Safeguard helps
Pod Security Admission enforces at runtime, but by then a non-compliant workload is already a rejected deploy and a frustrated engineer. Catching it earlier is cheaper. Safeguard's IaC scanning evaluates your Deployment and Pod manifests against the baseline and restricted profiles at review time, so a missing runAsNonRoot or an undropped capability is a pull-request comment instead of an admission failure in staging. Container security scanning verifies that the images those pods reference are non-root and minimal, so a runAsNonRoot: true policy does not fail simply because the image only ships a root user. Griffin AI then prioritizes remediation by combining posture with reachability, so a pod that is both over-privileged and running an exploitable CVE rises to the top. See how the platform compares in Safeguard vs Aqua.
PSP is gone; the standards that replaced it are simpler and stricter. Create a free Safeguard account or read the documentation to enforce them from code to cluster.