What Is Pod Security Admission in Kubernetes? (2026)
Pod Security Admission replaced PodSecurityPolicy in Kubernetes 1.25. Here's what it does, how the three security levels work, and how to enforce it in your cluster.
Pod Security Admission (PSA) is Kubernetes' built-in way to control what security settings pods can use. It replaced the deprecated PodSecurityPolicy in Kubernetes 1.25.
What Problem Does It Solve?
Without pod security controls, any workload in your cluster can run as root, mount the host filesystem, or use host networking — all major security risks.
PSA lets you set cluster-wide rules like:
- "No pod in this namespace can run as root"
- "No pod can use privileged mode"
- "All pods must have a non-root user and read-only filesystem"
The Three Security Levels
PSA has three levels, from least to most restrictive:
Privileged — no restrictions. Everything is allowed. Use this only for system namespaces (kube-system, gpu-operator, etc.).
Baseline — prevents known privilege escalations. Blocks:
- Privileged containers
- Host namespaces (hostPID, hostIPC, hostNetwork)
- Most HostPath volume mounts
- Running as root with certain capabilities
Good for general-purpose workloads.
Restricted — follows the hardening best practices. On top of Baseline, also requires:
- Running as non-root user
- Read-only root filesystem
- No privilege escalation
- Dropped all capabilities (only specific ones can be added back)
Good for production workloads handling sensitive data.
The Three Enforcement Modes
For each level, you can set three modes:
enforce — pods that violate the policy are rejected.
audit — violations are logged in the audit log but pods still run. Good for testing.
warn — violations trigger a warning shown to the user (kubectl apply shows a warning) but pods still run.
How to Apply PSA to a Namespace
PSA is applied via namespace labels:
apiVersion: v1
kind: Namespace
metadata:
name: my-app
labels:
# Enforce restricted policy
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
# Warn about restricted violations (same level, catches edge cases)
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: latest
# Audit restricted violations to the audit log
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: latestOr label an existing namespace:
kubectl label namespace my-app \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latestTest Before Enforcing
Don't jump straight to enforce. Use warn mode first:
# Label namespace with warn only
kubectl label namespace my-app \
pod-security.kubernetes.io/warn=restricted
# Try to deploy your app
kubectl apply -f deployment.yaml
# Warning: would violate "restricted" policy
# → tells you what needs fixing without blocking anythingFix all warnings, then switch to enforce.
A Compliant Pod (Restricted Level)
Here's what a pod that passes the restricted policy looks like:
apiVersion: v1
kind: Pod
metadata:
name: secure-app
namespace: my-app
spec:
securityContext:
runAsNonRoot: true # must not run as root
runAsUser: 1000 # specific non-root UID
runAsGroup: 3000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault # required for restricted
containers:
- name: app
image: myapp:latest
securityContext:
allowPrivilegeEscalation: false # must be false
readOnlyRootFilesystem: true # strongly recommended
capabilities:
drop:
- ALL # drop all capabilities
add:
- NET_BIND_SERVICE # only add back what you needWhat Breaks With Restricted Policy
Many standard containers fail restricted because they run as root by default. Common examples:
- nginx:latest — runs as root by default. Use
nginxinc/nginx-unprivilegedinstead. - redis — runs as root. Override with
securityContext.runAsUser: 999 - Most Helm charts — often haven't set security contexts properly
Fix for nginx:
containers:
- name: nginx
image: nginxinc/nginx-unprivileged:latest # already non-root
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]Cluster-Wide Default with Admission Configuration
Apply a default PSA level to all new namespaces:
# /etc/kubernetes/admission-configuration.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
configuration:
apiVersion: pod-security.admission.config.k8s.io/v1
kind: PodSecurityConfiguration
defaults:
enforce: "baseline"
enforce-version: "latest"
audit: "restricted"
audit-version: "latest"
warn: "restricted"
warn-version: "latest"
exemptions:
namespaces:
- kube-system
- gpu-operator
- cert-managerPSA vs Kyverno vs OPA Gatekeeper
PSA is built-in and zero-config but limited. For more control:
| Tool | Best for |
|---|---|
| Pod Security Admission | Simple cluster-wide baseline, no extra components |
| Kyverno | Rich policies, auto-mutation, easy YAML syntax |
| OPA Gatekeeper | Complex enterprise policies, Rego language |
Most teams use PSA as a first layer and Kyverno for custom policies on top.
Quick reference:
privileged— system namespaces onlybaseline— general workloads, safe defaultrestricted— production, sensitive workloads- Always use
warnfirst, thenenforce - Exempt
kube-systemand system namespaces from restrictions
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
How to Set Up HashiCorp Vault for Secrets Management from Scratch (2026)
HashiCorp Vault is the industry standard for secrets management. This step-by-step guide shows you how to install Vault, configure it, and integrate it with Kubernetes.
What is a Service Mesh? Explained Simply (No Jargon)
Service mesh sounds complicated but the concept is simple. Here's what it actually does, why teams use it, and whether you need one — explained without the buzzwords.
What is a ConfigMap and Secret in Kubernetes? Explained Simply (2026)
ConfigMaps and Secrets separate configuration from code in Kubernetes. Here's what they are, how they work, and when to use each one — explained simply.