PodSecurityPolicy was removed in Kubernetes 1.25 after years of being deprecated and, frankly, disliked — the admission logic was confusing to reason about and harder to debug when it rejected a pod for reasons that weren't obvious from the error message. Pod Security Admission is what replaced it: a built-in admission controller that enforces one of three predefined profiles per namespace, using nothing more than a label. No CRDs, no separate policy objects to manage — just labels on the namespace itself.
The three profiles
Pod Security Standards define three profiles, each stricter than the last. Privileged is unrestricted — it exists so cluster-admin workloads (CNI plugins, node agents) that genuinely need broad host access have somewhere to live without special-casing every namespace. Baseline blocks the most obviously dangerous settings — privileged containers, host namespaces, most custom capabilities — while staying compatible with common container images that haven't been hardened. Restricted is the one you actually want for application workloads: it enforces current pod hardening best practice, including running as non-root, dropping all Linux capabilities by default, and requiring a defined seccomp profile.
apiVersion: v1
kind: Namespace
metadata:
name: payments
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
enforce, audit, and warn are independent
Each profile can be applied in three modes at once, and they don't have to match. enforce actually rejects non-compliant pods at admission time. audit allows the pod but logs a policy violation to the audit log, useful for seeing what would break without breaking anything yet. warn allows the pod but returns a warning to whoever ran kubectl apply, visible in their terminal immediately. The practical pattern is to set warn and audit to restricted while leaving enforce at baseline or unset, watch the audit log for a release cycle to see what would actually get rejected, then flip enforce once you know nothing legitimate breaks.
Namespaces with existing workloads should not go straight to enforce: restricted. Set audit and warn first, review the audit log or your CI's kubectl output for violations over a real deploy cycle, fix what surfaces, then move enforce up. Skipping this step means finding out your restricted policy blocks a legitimate workload during an incident, not during a planned migration.
What restricted actually blocks
The restricted profile's checks map to real attack surface, not arbitrary rules. It blocks: containers running as root (runAsNonRoot must be true), any path to privilege escalation (allowPrivilegeEscalation: false is required), use of host namespaces (hostNetwork, hostPID, hostIPC must all be false), and any Linux capability beyond a small allowed set — capabilities.drop: ["ALL"] is required, with only narrowly scoped additions like NET_BIND_SERVICE permitted. It also requires a seccompProfile to be set, typically RuntimeDefault. Most of this maps directly onto pod-escape and privilege-escalation techniques that have shown up in real container breakout CVEs — the profile isn't guessing at best practice, it's closing specific doors.
apiVersion: v1
kind: Pod
metadata:
name: api
namespace: payments
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: api
image: registry.example.com/api:1.4.0
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
Migrating off PodSecurityPolicy
Teams still running clusters below 1.25, or that upgraded and left PSP admission webhooks half-removed, are the ones most likely to get bitten here — PSP is gone in 1.25+, full stop, and there's no compatibility shim. The Pod Security Standards profiles cover the same intent as a reasonably strict PSP but with a much smaller surface to configure: three labels per namespace instead of a cluster-wide policy object with role bindings to manage which pods can use which policy. If you're migrating an existing PSP setup, expect to spend most of the effort auditing which workloads were quietly relying on PSP gaps (a policy that was more permissive than anyone realized) rather than on the Pod Security Admission configuration itself.
Wrapping up
Pod Security Admission is a smaller, simpler mechanism than PodSecurityPolicy ever was — three profiles, three modes, applied through labels instead of a separate policy resource. The restricted profile is the one worth enforcing on any namespace running application workloads, but it's worth rolling out through audit and warn first, since the checks it enforces (non-root, no privilege escalation, no host namespaces, dropped capabilities) are exactly the settings that older or unhardened images tend to violate.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.