Introduction to Pod Security Standards: The Modern Pod Security Framework
Pod Security Standards (PSS) represent Kubernetes' modern, streamlined approach to enforcing security controls on pod specifications, replacing the deprecated and notoriously complex Pod Security Policies (PSP) that were removed in Kubernetes 1.25. While Pod Security Policies required complex admission controller configuration, ClusterRole and RoleBinding setup, and deep understanding of PodSecurityPolicy resource specifications that even experienced Kubernetes administrators found confusing and error-prone, Pod Security Standards take a radically simpler approach using just three well-defined security profiles Privileged, Baseline, and Restricted enforced through simple namespace labels that any engineer can understand and apply, making pod security accessible to teams beyond just Kubernetes security experts.
The three Pod Security Standard profiles form a progressive security hierarchy ranging from completely unrestricted to hardened production-grade security. The Privileged profile allows everything including privileged containers with root access, host namespace sharing, and dangerous volume types appropriate only for trusted system-level infrastructure like CNI plugins, CSI storage drivers, or monitoring agents that require host access to function. The Baseline profile prevents known privilege escalation paths by disallowing privileged containers, restricting hostPath volumes, preventing host network/PID/IPC namespace sharing, and limiting dangerous Linux capabilities, providing reasonable security for legacy applications or migration scenarios where full hardening isn't immediately feasible. The Restricted profile implements comprehensive production-grade hardening requiring containers run as non-root users, dropping all Linux capabilities, preventing privilege escalation, using restricted volume types only, requiring seccomp profiles for syscall filtering, and enforcing read-only root filesystems this is the recommended profile for all production application workloads handling customer data or processing sensitive information.
The enforcement mechanism is elegantly simple compared to PSP's complexity: instead of creating PodSecurityPolicy resources, ClusterRoles, and RoleBindings, you simply add labels to namespaces specifying which security profile to enforce (pod-security.kubernetes.io/enforce=restricted), which violations to audit to logs (audit=restricted), and which violations to warn users about without blocking (warn=restricted). The built-in Pod Security Admission controller reads these namespace labels and automatically enforces, audits, or warns about pod specifications that violate the selected profile's requirements, providing immediate feedback during kubectl apply before pods are created rather than after runtime when troubleshooting is harder.
However, implementing Pod Security Standards correctly in production environments with diverse application portfolios is more nuanced than simply labeling namespaces. You must understand what each profile actually requires and prohibits with precise technical details, know how to migrate legacy applications written before PSS existed and potentially running as root or using privileged containers, handle edge cases where legitimate applications need capabilities or configurations that Restricted profile blocks, configure proper fallback and exception mechanisms for system workloads that cannot meet Restricted requirements, avoid breaking production during rollout through careful testing and gradual enforcement, and maintain compliance over time as new applications deploy and existing ones evolve.
This comprehensive guide teaches you everything about Pod Security Standards implementation from concept to production, covering: deep technical explanation of the three profiles (Privileged, Baseline, Restricted) with exact requirements and what each blocks, complete migration path from deprecated Pod Security Policies with zero downtime, step-by-step implementation guide with testing strategies to avoid breaking production services, fixing common pod specification violations that Restricted profile catches (running as root, missing security contexts, privileged containers), handling legitimate exceptions for system workloads and infrastructure components, gradual rollout strategy using warn → audit → enforce progression, troubleshooting pod admission failures with clear error messages, maintaining PSS compliance as applications evolve over time with CI/CD integration and admission webhook validation, and how Atmosly automates Pod Security Standards implementation by auto-applying appropriate profile labels per environment (dev=baseline, prod=restricted), validating deployments before they reach production catching violations early in CI/CD pipelines, providing specific fix suggestions with exact YAML changes needed to pass Restricted profile requirements, offering compliance dashboards showing which namespaces meet which profiles with violation details, assisting with PSP migration through automated policy analysis and conversion, and providing template library of Restricted-compliant deployment manifests for common application patterns eliminating trial-and-error YAML editing.
By mastering Pod Security Standards through this guide, you'll be able to implement production-grade pod hardening across your Kubernetes infrastructure, meet compliance requirements for container security, prevent common container escape and privilege escalation attacks, migrate smoothly from deprecated Pod Security Policies, and establish sustainable security governance that scales as your cluster grows.
Understanding the Three Pod Security Standard Profiles
Profile 1: Privileged (Unrestricted - System Infrastructure Only)
Security Level: None—allows everything with zero restrictions
What it allows:
- ✅ Privileged containers (
privileged: true) with full host access - ✅ Host namespace sharing (hostNetwork, hostPID, hostIPC)
- ✅ Any volume types including hostPath mounting host filesystem
- ✅ All Linux capabilities including dangerous ones (CAP_SYS_ADMIN)
- ✅ Running as root (UID 0)
- ✅ Privilege escalation allowed
- ✅ Any security context settings or none at all
Appropriate use cases (VERY limited):
- CNI network plugins (Calico, Cilium) needing to configure host networking
- CSI storage drivers mounting host filesystems and managing volumes
- DaemonSet monitoring agents (node-exporter) reading host metrics
- System maintenance pods requiring full node access for debugging or repair
- kube-proxy, kube-dns/CoreDNS if running as pods (often run as host processes instead)
⚠️ WARNING: Never use Privileged profile for application workloads. This profile provides NO security and should only exist in kube-system or dedicated system namespaces. If application teams request Privileged profile, the answer should be "NO" 99.9% of the time—investigate why they think they need it and find alternative solutions.
Profile 2: Baseline (Minimally Restrictive - Migration Path)
Security Level: Prevents known privilege escalation techniques and most dangerous configurations
What Baseline PROHIBITS:
- ❌ Privileged containers (
privileged: truenot allowed) - ❌ Sharing host namespaces (hostNetwork, hostPID, hostIPC all forbidden)
- ❌ hostPath volumes (with limited exceptions for read-only system paths like /etc/hosts)
- ❌ hostPorts below 1024 (privileged ports)
- ❌ Adding dangerous Linux capabilities (CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_SYS_MODULE, etc.)
- ❌ Using ALL capability wildcard
- ❌ Unsafe AppArmor profiles
- ❌ Unsafe SELinux type labels
- ❌ Unsafe sysctls (kernel parameters)
What Baseline ALLOWS (that Restricted does not):
- ✅ Running as root (runAsUser: 0 is permitted, though discouraged)
- ✅ Privilege escalation allowed by default (allowPrivilegeEscalation can be true)
- ✅ No seccomp profile requirement
- ✅ Writable root filesystem
- ✅ Most volume types (ConfigMap, Secret, EmptyDir, PersistentVolumeClaim, etc.)
- ✅ Some Linux capabilities allowed (NET_BIND_SERVICE, CHOWN, DAC_OVERRIDE, etc.)
Appropriate use cases:
- Staging or development environments during migration from unrestricted pods
- Legacy applications that require root or specific capabilities but don't need full privilege
- Intermediate step in PSP migration before achieving Restricted compliance
- Third-party applications not yet updated for Restricted profile compatibility
Baseline is NOT recommended for production long-term. It's a stepping stone toward Restricted, not a destination. Use Baseline temporarily during migration, then move to Restricted.
Profile 3: Restricted (Hardened - Production Standard)
Security Level: Comprehensive production-grade hardening following security best practices
What Restricted REQUIRES (in addition to Baseline prohibitions):
- ✅ MUST run as non-root:
runAsNonRoot: trueANDrunAsUser > 0 - ✅ MUST drop ALL Linux capabilities:
capabilities: drop: [ALL] - ✅ MUST prevent privilege escalation:
allowPrivilegeEscalation: false - ✅ MUST define seccomp profile:
seccompProfile: type: RuntimeDefaultor Localhost - ✅ MUST use restricted volume types only (configMap, downwardAPI, emptyDir, persistentVolumeClaim, projected, secret)
- ✅ Should use read-only root filesystem (readOnlyRootFilesystem: true) though not strictly required
Example Restricted-compliant pod:
apiVersion: v1
kind: Pod
metadata:
name: secure-app
namespace: production
spec:
securityContext:
runAsNonRoot: true # Required
runAsUser: 1000 # Required (>0)
runAsGroup: 3000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault # Required
containers:
- name: app
image: my-app:v1
securityContext:
allowPrivilegeEscalation: false # Required
readOnlyRootFilesystem: true # Recommended
capabilities:
drop:
- ALL # Required
# Need writable directories for app temp files
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache
volumes:
- name: tmp
emptyDir: {} # Allowed volume type
- name: cache
emptyDir: {}
Why Restricted is the production standard:
- Prevents container escape attacks (running as root with capabilities enables escapes)
- Limits privilege escalation (setuid binaries can't elevate privileges)
- Reduces attack surface (no unnecessary Linux capabilities)
- Enforces immutable infrastructure (read-only filesystem)
- Meets compliance requirements (SOC 2, PCI-DSS, HIPAA)
- Defense-in-depth security (multiple barriers against compromise)
Recommendation: All production namespaces running application workloads should enforce Restricted profile. Period.
Pod Security Standards Enforcement Modes
PSS provides three enforcement modes applied via namespace labels, enabling gradual rollout and testing before hard enforcement:
Mode 1: Enforce (Block Non-Compliant Pods)
Label: pod-security.kubernetes.io/enforce=restricted
Behavior: Admission controller BLOCKS creation of pods violating Restricted profile requirements. kubectl apply fails with clear error message explaining violation.
Use for: Production namespaces after testing and fixing all violations
Mode 2: Audit (Log Violations)
Label: pod-security.kubernetes.io/audit=restricted
Behavior: Allows pod creation but adds audit log entry for violations. No user-facing error.
Use for: Identifying violations without breaking deployments, monitoring compliance trends
Mode 3: Warn (Show Warnings)
Label: pod-security.kubernetes.io/warn=restricted
Behavior: Allows pod creation but shows warning message to user during kubectl apply. Warning describes violation.
Use for: Educating developers about violations, encouraging fixes without blocking deployments
Combining Modes for Gradual Rollout
# Phase 1: Warn only (educate, don't block)
kubectl label namespace production \\
pod-security.kubernetes.io/warn=restricted
# Developers see warnings when deploying non-compliant pods
# Fix pods to address warnings
# Phase 2: Add audit (track violations)
kubectl label namespace production \\
pod-security.kubernetes.io/audit=restricted \\
--overwrite
# Violations logged for compliance tracking
# Monitor audit logs to identify remaining issues
# Phase 3: Enforce (block violations)
kubectl label namespace production \\
pod-security.kubernetes.io/enforce=restricted \\
--overwrite
# Non-compliant pods now blocked
# Only compliant pods can deploy
Complete Implementation Guide
Step 1: Audit Current Pod Security Posture
Before implementing PSS, understand what you're dealing with:
# Check if any pods run as root
kubectl get pods --all-namespaces -o json | \\
jq '.items[] | select(.spec.securityContext.runAsNonRoot != true) |
{namespace: .metadata.namespace, pod: .metadata.name, runAsUser: .spec.securityContext.runAsUser}'
# Check for privileged containers
kubectl get pods --all-namespaces -o json | \\
jq '.items[].spec.containers[] | select(.securityContext.privileged == true)'
# Check for hostNetwork usage
kubectl get pods --all-namespaces -o json | \\
jq '.items[] | select(.spec.hostNetwork == true)'
Step 2: Apply PSS in Warn Mode First
# Start with warnings only (no blocking)
kubectl label namespace production \\
pod-security.kubernetes.io/warn=restricted \\
pod-security.kubernetes.io/warn-version=latest
# Deploy your applications
kubectl apply -f my-app-deployment.yaml
# You'll see warnings like:
# Warning: would violate PodSecurity "restricted:latest":
# allowPrivilegeEscalation != false (container "app" must set allowPrivilegeEscalation=false)
# runAsNonRoot != true (pod must set securityContext.runAsNonRoot=true)
# seccompProfile (pod must set securityContext.seccompProfile.type)
Step 3: Fix Pod Specifications
Common violations and fixes:
Violation 1: Missing runAsNonRoot
# Error message:
# runAsNonRoot != true (pod must set securityContext.runAsNonRoot=true)
# Fix:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000 # Specific non-root UID
Violation 2: Missing allowPrivilegeEscalation=false
# Error:
# allowPrivilegeEscalation != false
# Fix (container-level):
containers:
- name: app
securityContext:
allowPrivilegeEscalation: false
Violation 3: Missing seccomp profile
# Error:
# seccompProfile (pod must set securityContext.seccompProfile.type)
# Fix (pod-level):
spec:
securityContext:
seccompProfile:
type: RuntimeDefault # Or Localhost with custom profile
Violation 4: ALL capabilities not dropped
# Error:
# unrestricted capabilities
# Fix:
containers:
- name: app
securityContext:
capabilities:
drop:
- ALL
# Only add if absolutely necessary:
# add:
# - NET_BIND_SERVICE # For binding to port <1024
Violation 5: Writable root filesystem
# While not strictly required by Restricted, recommended
# Fix:
containers:
- name: app
securityContext:
readOnlyRootFilesystem: true
# Provide emptyDir for writable paths
volumeMounts:
- name: tmp
mountPath: /tmp
- name: var-cache
mountPath: /var/cache
volumes:
- name: tmp
emptyDir: {}
- name: var-cache
emptyDir: {}
Step 4: Enable Audit Mode
# Add audit (logs violations to API audit log)
kubectl label namespace production \\
pod-security.kubernetes.io/audit=restricted \\
--overwrite
# Check audit logs for violations
# (requires API audit logging configured)
Step 5: Enable Enforcement
Only after ALL pods fixed and warnings resolved:
# Enable enforcement (blocks non-compliant pods)
kubectl label namespace production \\
pod-security.kubernetes.io/enforce=restricted \\
--overwrite
# Test deployment
kubectl apply -f app-deployment.yaml
# If non-compliant, admission denied with error message
# Fix and retry
Migrating from Pod Security Policies to Pod Security Standards
Why PSP Was Deprecated
Pod Security Policies had fundamental design flaws:
- ❌ Complex admission controller requiring ClusterRole/RoleBinding setup
- ❌ Confusing RBAC model where users need permission to "use" PSPs
- ❌ No clear defaults—clusters with PSP enabled but no policies block all pods
- ❌ Difficult to reason about which PSP applies when multiple match
- ❌ No gradual rollout—either enforced or not
- ❌ Poor error messages making troubleshooting hard
PSP was deprecated in Kubernetes 1.21 and removed completely in 1.25. All clusters must migrate to PSS.
Migration Strategy: Zero-Downtime Approach
Phase 1: Assessment (Week 1)
- Audit current PSP usage:
kubectl get psp - Map PSPs to PSS levels (most map to baseline or restricted)
- Identify pods using each PSP
- Document special cases requiring privileged access
Phase 2: PSS Warn Mode (Week 2)
# Apply PSS labels in warn mode to all namespaces
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
kubectl label namespace $ns \\
pod-security.kubernetes.io/warn=baseline \\
--overwrite
done
# System namespaces get privileged
kubectl label namespace kube-system \\
pod-security.kubernetes.io/warn=privileged \\
--overwrite
Phase 3: Fix Violations (Week 3-4)
Update pod specs based on warnings, test thoroughly in dev/staging, validate production deployments work
Phase 4: Enable PSS Audit (Week 5)
kubectl label namespace production \\
pod-security.kubernetes.io/audit=baseline \\
--overwrite
Phase 5: Enable PSS Enforcement (Week 6)
kubectl label namespace production \\
pod-security.kubernetes.io/enforce=baseline \\
--overwrite
# Upgrade to restricted once baseline working
kubectl label namespace production \\
pod-security.kubernetes.io/enforce=restricted \\
--overwrite
Phase 6: Remove PSPs (Week 7)
# Only after PSS fully enforced everywhere
kubectl delete psp --all
Handling Exceptions and Edge Cases
System Workloads Requiring Privileged Profile
Some components legitimately need privileged access:
# kube-system gets privileged profile
kubectl label namespace kube-system \\
pod-security.kubernetes.io/enforce=privileged
# Monitoring namespace might need baseline for node-exporter
kubectl label namespace monitoring \\
pod-security.kubernetes.io/enforce=baseline
Per-Workload Exceptions (Not Recommended)
PSS applies per-namespace, not per-pod. You cannot exempt specific pods.
Workaround if absolutely necessary:
- Create separate namespace with lower security profile for exceptional workload
- Document WHY this exception exists
- Review quarterly whether still needed
- Implement additional compensating controls (Network Policies, RBAC restrictions)
Troubleshooting Pod Admission Failures
Error: "would violate PodSecurity restricted"
Admission blocked with detailed error message:
Error from server (Forbidden): error when creating "deployment.yaml":
pods "my-app-abc123" is forbidden: violates PodSecurity "restricted:latest":
allowPrivilegeEscalation != false (container "app" must set allowPrivilegeEscalation=false),
runAsNonRoot != true (pod or container "app" must set securityContext.runAsNonRoot=true),
seccompProfile (pod or container "app" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
Solution: Error message tells you exactly what to fix. Add required fields to pod spec.
How Atmosly Automates Pod Security Standards
Environment-Based Automatic Profile Application
Atmosly automatically applies appropriate PSS profiles based on environment classification:
- Production namespaces:
enforce=restricted(maximum security) - Staging namespaces:
enforce=baseline(reasonable security for testing) - Development namespaces:
warn=baseline(educate without blocking) - System namespaces:
enforce=privileged(required for infrastructure)
This eliminates manual labeling and ensures consistent security policy across clusters.
Pre-Deployment Validation in CI/CD
Atmosly validates deployments before they reach production:
- CI/CD pipeline runs deployment through Atmosly validation API
- Atmosly checks if deployment would pass target namespace PSS profile
- If violations found, CI/CD fails with specific errors
- Developers fix violations before merge
- Production deployments always compliant
Catches security issues early in development, not during production deployment failures.
Automated Fix Suggestions
When Atmosly detects PSS violations, provides exact YAML changes needed:
Pod Security Violation Detected
Deployment: payment-service
Namespace: production
Profile: restricted
Status: ❌ Non-compliantViolations:
- Missing runAsNonRoot in pod securityContext
- Missing allowPrivilegeEscalation=false in container securityContext
- Missing seccompProfile
Apply this fix:
spec: template: spec: securityContext: runAsNonRoot: true runAsUser: 1000 seccompProfile: type: RuntimeDefault containers: - name: app securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL
Pod Security Standards Best Practices
1. Use Restricted for All Production Application Workloads
Default to highest security. Exceptions require documented justification.
2. Implement Gradual Rollout: Warn → Audit → Enforce
Don't jump straight to enforcement—break production first, fix later is bad strategy.
3. System Namespaces Need Privileged
kubectl label namespace kube-system \\
pod-security.kubernetes.io/enforce=privileged
4. Document All Exceptions with Business Justification
If namespace uses baseline instead of restricted, document why in namespace annotations:
metadata:
annotations:
pod-security-exception: "Legacy app requires root for file permissions,
migration to non-root scheduled Q2 2025,
approved by security team 2025-01-15"
5. Review and Update Quarterly
Pod Security isn't set-and-forget. Review every quarter:
- Are all production namespaces still enforcing restricted?
- Can any baseline exceptions be upgraded to restricted?
- Are new namespaces getting proper labels?
- Are audit logs showing any violations to investigate?
Conclusion: Modern Pod Security Done Right
Pod Security Standards provide elegant, simple security for Kubernetes pods. Three profiles (privileged, baseline, restricted), namespace labels for enforcement, and clear error messages make pod security accessible to all engineers, not just security specialists.
Implementation Checklist:
- ✅ Apply restricted profile to all production namespaces
- ✅ Use baseline for staging during migration
- ✅ System namespaces (kube-system) get privileged
- ✅ Implement gradual rollout: warn → audit → enforce
- ✅ Fix pod specs to comply with restricted requirements
- ✅ Document exceptions with business justification
- ✅ Review compliance quarterly
- ✅ Use Atmosly for automated enforcement and validation
Ready to implement production-grade pod security without breaking deployments? Start your free Atmosly trial for automated Pod Security Standards with pre-deployment validation and compliance reporting.