Migrating to Kubernetes Pod Security Standards

7 min read
KubernetesSecurityPod SecurityDevSecOpsMigrationPlatform Engineering

PodSecurityPolicy (PSP) was deprecated in Kubernetes 1.21 and removed entirely in 1.25, yet a surprising number of production clusters still run without a formal pod-level enforcement mechanism. The built-in replacement — Pod Security Admission (PSA) — has been stable since 1.25 and requires no external controllers, no webhooks, and no additional policy engine. But migrating from PSP to PSA is not a drop-in replacement: the enforcement model is fundamentally different, the namespace labelling approach replaces cluster-wide policies, and the cultural shift from permissive-by-default to deny-by-default requires deliberate planning. This guide explains Pod Security Standards, the PSA controller, and a practical step-by-step migration that you can adapt to your own clusters — whether you are running three namespaces or three hundred.

## Why PodSecurityPolicy Was Deprecated

The Kubernetes community deprecated PodSecurityPolicy for several well-documented reasons. PSPs were operationally difficult to configure correctly — the ordering of policies was non-deterministic, mutation capabilities created confusing side effects, and debugging why a pod was rejected required tracing through multiple overlapping policies. The Kubernetes Enhancement Proposal that introduced Pod Security Admission cited these usability problems as primary motivations for replacing PSP with a simpler, built-in mechanism. PSPs also created a false sense of security: many teams enabled the restricted policy but then granted the use privilege too broadly, effectively neutering the intended enforcement.

## Understanding Pod Security Standards

Pod Security Standards define three policy levels that are built directly into the Kubernetes API: privileged, baseline, and restricted. The privileged level is entirely unrestricted — it is intended for system and infrastructure workloads that genuinely need host-level access, such as CNI plugins, CSI drivers, and logging agents that read from host paths. The baseline level blocks known privilege escalations while remaining compatible with most common application workloads; it prohibits hostPath volumes, privilege escalation, and running as root, but still permits hostPorts and non-default capabilities. The restricted level enforces current pod hardening best practices and is the recommended target for production application namespaces — it requires running as a non-root user, drops all capabilities, and restricts volume types to a curated set. The Kubernetes documentation provides a detailed breakdown of exactly which fields are allowed or disallowed at each level.

## Enforcing Standards with Pod Security Admission

Pod Security Admission is a built-in admission controller that enforces Pod Security Standards at the namespace level. Unlike PSP — which applied cluster-wide and then relied on RBAC to determine which policies applied to which subjects — PSA uses namespace labels to assign a policy level and mode. Each namespace can be labelled with a policy level for three independent modes — enforce, audit, and warn — giving platform teams granular control over the rollout. The enforcement decision is made at admission time by the kube-apiserver itself, with no external webhook latency and no additional failure domain.

The following namespace labels apply the restricted profile in enforce mode and also enable audit and warn logging for full visibility: ```yaml apiVersion: v1 kind: Namespace metadata: name: production-app labels: pod-security.kubernetes.io/enforce: restricted pod-security.kubernetes.io/enforce-version: v1.30 pod-security.kubernetes.io/audit: restricted pod-security.kubernetes.io/audit-version: v1.30 pod-security.kubernetes.io/warn: restricted pod-security.kubernetes.io/warn-version: v1.30 ```

### Admission Mode Behaviors

Each mode behaves differently under the same policy level. The enforce mode rejects any pod that violates the policy — this is the production safety net. The audit mode logs violations to the Kubernetes audit log without blocking the pod, making it ideal for discovering which workloads need remediation before turning on enforcement. The warn mode sends a warning message to the user who submitted the pod — visible in kubectl output and API responses — but allows the pod to be created. Warn mode is the gentlest introduction and is suitable for development namespaces and during the early stages of migration when you want to raise awareness without disrupting developer workflows.

## Step-by-Step Migration Plan

Migrating from PSP to PSA should follow a phased approach that minimises disruption to running workloads. The core principle is to start with the least invasive mode (audit) and progressively increase enforcement as workloads are validated against each policy level. The following three-phase plan works for most clusters, but larger environments may benefit from applying it on a per-namespace or per-team cadence rather than attempting a cluster-wide migration all at once.

### Phase 1: Audit First

Begin by applying the restricted audit label to your target namespaces without any enforcement. This logs every pod that would be rejected by the restricted policy without actually blocking anything. After running in this state for at least one full release cycle — or one week, whichever is longer — review the audit logs to identify which workloads need attention. The audit log entries include the specific field and value that triggered the violation, making remediation straightforward. For workloads that genuinely require elevated privileges — such as monitoring agents that need hostPath access or CI/CD runners that need privileged mode — plan to keep them in separate namespaces that use the baseline or privileged level rather than trying to force them into restricted mode.

### Phase 2: Warn Mode

Add the warn label to namespaces that passed audit without violations, and keep the audit label on namespaces that still have outstanding violations. The warn mode surfaces policy violations to developers at submission time — they see a warning in their terminal or CI output — which raises awareness without blocking work. This phase typically runs for one sprint cycle and serves as a final validation that the team is ready for enforcement. If you use infrastructure-as-code to manage namespace labels, add the warn labels to your Terraform or Pulumi configuration at this stage so they persist across namespace recreation.

### Phase 3: Enforce Gradually

Once a namespace has passed audit and warn review for a full cycle, add the enforce label. Start with lower-risk namespaces such as development and staging before moving to production. If a pod is rejected by enforce mode, the error message from the kube-apiserver includes the exact policy violation, making debugging fast. The following command tests whether a specific pod spec would pass the restricted policy without actually submitting it — useful for CI/CD pre-flight checks: ```sh # Validate a pod manifest against the restricted profile kubectl label --dry-run=server --overwrite ns my-namespace \ pod-security.kubernetes.io/enforce=restricted ```

## Handling Exceptions and Privileged Workloads

Not every workload belongs in a restricted namespace. CNI plugins, CSI drivers, monitoring agents, and security tools such as Falco often need hostPath volumes, privileged containers, or host network access. Instead of creating cluster-wide exemptions — which was the PSP anti-pattern — designate dedicated namespaces for privileged workloads and label them accordingly. A typical cluster might have a kube-system namespace at the privileged level, an observability namespace at the baseline level, and all application namespaces at the restricted level. Your <a href="/blog/kubernetes-rbac-least-privilege-guide/">RBAC configuration</a> should then restrict access to the privileged namespaces so that only platform operators can deploy into them, preventing developers from accidentally or intentionally running privileged pods outside the designated namespaces.

## Integrating with Policy-as-Code Tools

Pod Security Admission provides a solid built-in baseline, but it is limited to what the three policy levels can express. For finer-grained controls — requiring specific Linux capabilities, enforcing image registry policies, or blocking containers that run as root within a restricted namespace — policy-as-code tools such as Kyverno or OPA Gatekeeper complement PSA. These tools can enforce rules that the built-in standards do not cover, such as requiring all container images to come from a trusted registry or ensuring that every pod has resource requests and limits. PSA handles the majority of pod security concerns at zero additional operational cost; policy engines fill the remaining gap for teams that need deeper customisation. Together with <a href="/blog/kubernetes-network-policies-zero-trust-networking/">network policies</a> that restrict pod-to-pod communication, PSA forms one layer of a defence-in-depth strategy for cluster security.

## Migration Checklist

Use the following checklist to track your migration progress across each namespace in your cluster: - Audit all current PSPs in the cluster with kubectl get psp and kubectl get clusterroles that reference them. - Identify every namespace and classify it as restricted (standard apps), baseline (ops tooling), or privileged (system components). - Apply audit labels to every application namespace and let them run for at least one release cycle. - Review audit logs for violations and remediate workloads that can be adjusted without operational impact. - Apply warn labels and run for at least one sprint cycle, confirming that developers see and understand the warnings. - Apply enforce labels starting with staging, then lower-risk production namespaces, and finally high-traffic production. - Document which namespaces remain at baseline or privileged and enforce RBAC to restrict deployment access to those namespaces. - Integrate a pod security validation step into your CI/CD pipeline so that violations are caught before deployment.

Pod Security Admission is the lowest-cost pod security enforcement mechanism available today — it ships with every Kubernetes cluster running 1.25 or later and requires no additional infrastructure. If your team needs help auditing your current pod security posture, designing a migration plan, or integrating PSA with your existing policy-as-code and CI/CD pipeline, <a href="/">Secpros</a> can review your clusters and return a short, prioritised remediation plan.

## Sources

The Pod Security Standards and Pod Security Admission mechanisms described in this post are documented in the Kubernetes [Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/) and [Pod Security Admission](https://kubernetes.io/docs/concepts/security/pod-security-admission/) documentation. The history of PodSecurityPolicy deprecation and the Kubernetes Enhancement Proposal that introduced PSA are tracked in [KEP-2579: Pod Security Admission](https://github.com/kubernetes/enhancements/tree/master/keps/sig-auth/2579-psp-replacement).

/ author

Pawel Bedynski

DevOps Engineer & Kubernetes Consultant. Building cloud-native infrastructure on GCP since 2019. 80+ production clusters deployed.

LinkedIn