Safeguard
Container Security

Pod Security Standards: The Complete Guide

PodSecurityPolicy is gone. Pod Security Admission and the three Pod Security Standards are how you enforce baseline and restricted profiles in modern Kubernetes — here is how to adopt them without breaking workloads.

Marcus Chen
Cloud Security Engineer
5 min read

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, no hostPath volumes, 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 ALL capabilities, must set seccompProfile to RuntimeDefault, 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.

  1. Inventory in audit mode cluster-wide. Label namespaces with audit: restricted and warn: restricted and leave enforce off. Collect the audit annotations for a week to see exactly which workloads fail and why.
  2. Fix the easy wins. Most failures are missing securityContext fields, not genuine needs for privilege. Add the non-root and capability-drop settings.
  3. Enforce baseline first. It is a low bar that catches the truly dangerous pods and rarely breaks legitimate apps.
  4. Promote namespaces to enforce restricted one at a time. Start with new or low-risk namespaces; leave the messy legacy namespace for last.
  5. Exempt infrastructure deliberately. Workloads that legitimately need privilege (CNI, CSI drivers) go in dedicated privileged namespaces, 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

RestrictionBaselineRestricted
privileged: trueBlockedBlocked
Host namespaces / hostPathBlockedBlocked
Run as non-rootAllowed as rootRequired
Drop ALL capabilitiesNot requiredRequired
seccompProfile: RuntimeDefaultNot requiredRequired
allowPrivilegeEscalation: falseNot requiredRequired

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.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.