Skip to content
DevOps AI ToolKit
All guides
AI for Automation By James Joyner IV · · 13 min read

DevOps: Enforce Pod Security Standards With CI and AI Checks

DevOps steps to roll out Kubernetes pod security standards safely. CI- and AI-assisted checks, dry-run tests, and a staged audit-to-enforce path.

DevOps: Enforce Pod Security Standards With CI and AI Checks

Pod Security Standards are the three Kubernetes-defined profiles, Privileged, Baseline, and Restricted, enforced through the built-in Pod Security Admission controller. PSA is validating only, non-mutating, and has been generally available since Kubernetes v1.25. Our recommended starting move: turn on audit and warn first, watch what breaks, then enforce per namespace once you trust the results.


TL;DR:

  • Most workloads should be migrated gradually from the Privileged profile to Baseline and then to Restricted, focusing on high-risk and externally-facing applications.
  • Setting audit and warn policies before enforcement helps identify non-compliant workloads and reduces deployment failures.
  • Key controls to inspect include seccomp profiles, capabilities, runAsNonRoot, readOnlyRootFilesystem, volume types, and sysctls, with dropping all capabilities and avoiding Unconfined profiles as priorities.
  • PSA only validates against fixed profiles and does not mutate pods, necessitating additional tools like Webhooks or policy engines for comprehensive mutation and customization.
  • A thorough migration often involves auditing current PSPs, mapping their fields to Pod Security Standards, and testing policies in non-production environments for smooth enforcement.

Table of Contents

What Privileged, Baseline, and Restricted Actually Allow

Each profile is a different line in the sand, and picking the right one per namespace matters more than picking the strictest one everywhere. We have watched teams enforce Restricted cluster-wide on day one and spend the next two weeks debugging legitimate workloads that simply needed a node-level agent.

Privileged is the profile with effectively no restrictions. It exists for system-level and infrastructure workloads, things like CNI plugins, storage drivers, or monitoring agents that genuinely need host access. Treat it as an exception namespace, not a default.

Baseline blocks the privilege escalations security teams actually worry about, while staying permissive enough for most everyday application pods. It is the sensible floor for namespaces running typical business logic.

Restricted is where the real hardening lives. According to the Kubernetes Pod Security Standards, this profile requires runAsNonRoot, mandates dropping all Linux capabilities, and forbids the Unconfined seccomp profile. It is built for workloads handling sensitive data or exposed to untrusted input.

A few operational points worth internalizing before you start labeling namespaces:

  • Privileged suits infrastructure and system daemons, never general application traffic.
  • Baseline fits most business applications without requiring a rewrite of existing manifests.
  • Restricted is appropriate for high-risk or externally-facing workloads where the blast radius of a compromise matters most.
  • Some Restricted controls are operating-system specific and only apply when pod.spec.os.name is not set to windows, so mixed-OS clusters need a bit of extra care when mapping policy to node pools.

The profile choice is not permanent. Many teams start a namespace at Baseline, run it there for a few release cycles, then graduate it to Restricted once they have confirmed nothing depends on root access or extra capabilities. That staged approach costs little and avoids the all-or-nothing trap.

How Pod Security Admission Applies These Profiles

PSA reads namespace labels, not cluster-wide flags, which means the profile you want lives right on the namespace object. Three label keys do the work: pod-security.kubernetes.io/enforce, pod-security.kubernetes.io/audit, and pod-security.kubernetes.io/warn. Each accepts a profile name, and you can optionally pin a Kubernetes minor version so the policy definition does not shift under you on upgrade.

The enforcement best practices from Kubernetes are explicit that audit and warn should go on before enforce ever does, and that the two are independent switches. Set warn without audit and you get developer-facing messages but no server-side record. Set audit without warn and you get a paper trail nobody sees until they go looking for it. You need both to close the visibility gap.

A workable rollout sequence looks like this:

  1. Label the namespace with audit and warn set to your target profile, leaving enforce unset or at Privileged.
  2. Run normal traffic for a defined soak period and collect audit log entries.
  3. Fix any workloads the audit and warn output flags as non-compliant.
  4. Flip enforce to the target profile once violations drop to zero.
Label keyPurposeTypical starting value
KubernetesBlocks non-compliant podsprivileged (then raised later)
KubernetesLogs violations without blockingbaseline or restricted
pod-security.kubernetes.io/warnShows kubectl warnings to usersbaseline or restricted

Cluster-level defaults can also be set through the API server’s admission configuration file, though the exact apiVersion varies by release, with pod-security.admission.config.k8s.io/v1 used in v1.25. System namespaces like kube-system are commonly exempted from enforcement since they run infrastructure pods that need Privileged-level access. If you are running an older API version, confirm the admission config schema matches before you copy an example from current docs, since field names have moved between releases.

For a deeper look at how admission control ties into broader cluster security, our overview of pod security and admission control walks through the mechanics in more detail.

Migrating From PodSecurityPolicy to Pod Security Admission

PodSecurityPolicy is deprecated, and the migration path is mechanical but unforgiving if you skip steps. The official migration guide lays out the core problem clearly: PSP could mutate pod specs to make them compliant, and PSA cannot. Anything your PSPs were silently fixing for you will now simply fail enforcement.

Comparison of PSP mutation and PSA enforcement

Before you touch the PSA labels, map every PSP field in use to its PSS equivalent and flag the fields PSS has no opinion on at all, things like host path allowlists or specific volume plugin restrictions that PSP controlled but PSS does not address. Those gaps need a different tool, which we cover in the limitations section below.

A migration sequence that has held up for us across several clusters:

  • Inventory active PSPs and the fields each one sets, separating mutating fields from purely validating ones.
  • Remove or neutralize mutating behavior first, updating manifests so they already comply with the target PSS profile without help.
  • Clone your current policy behavior into PSS labels on a non-production namespace and dry-run deployments against it.
  • Move to audit and warn mode cluster-wide, watching logs for workloads still relying on now-removed mutation.
  • Only then disable PSPs and switch affected namespaces to enforce.

Running kubectl apply --dry-run=server against representative manifests before changing any label catches most surprises early. Teams that skip the “clone offline” step tend to discover their mutation dependency in production, which is a far more expensive place to learn it.

Commands and Manifests to Test PSS Safely

Testing PSS on a live cluster does not require a maintenance window if you sequence it correctly. Start with labels only, verify behavior, then tighten.

  1. Label a target namespace for audit and warn without enforcing: kubectl label --overwrite ns my-namespace pod-security.kubernetes.io/audit=restricted pod-security.kubernetes.io/warn=restricted.
  2. Pin the version alongside the profile if you want stability across upgrades, for example pod-security.kubernetes.io/audit-version=latest.
  3. Submit a representative manifest with kubectl apply --dry-run=server -f pod.yaml and read the returned warnings directly in your terminal.
  4. Check the audit log or your log aggregator for pod-security.kubernetes.io/audit-violations annotations, which record what would have been blocked.
  5. List namespaces missing any PSS labels with kubectl get ns -o json | jq '.items[] | select(.metadata.labels["pod-security.kubernetes.io/enforce"] == null)' so nothing slips through unlabeled.
  6. Once audit logs stay clean for your chosen soak window, flip enforce to match.

Pro Tip: Run audit and warn for at least one full deployment cycle, including your slowest-moving batch jobs, before enforcing. Fast-moving services will look compliant in a day; the one cron job that runs monthly is what bites you later.

Warn messages show up to whoever runs kubectl apply, which is useful for catching issues during development, but audit annotations are what you want for a permanent record across a team that may not read terminal output closely.

Checklist of Controls to Set or Inspect

Most PSS failures trace back to a short, repeatable list of manifest fields. Reviewing these before enforcement saves far more time than debugging after a blocked deployment.

  • Set seccompProfile.type to RuntimeDefault or a named Localhost profile; Unconfined is explicitly forbidden under Restricted.
  • Drop ALL capabilities in securityContext.capabilities.drop, adding back only NET_BIND_SERVICE when a workload genuinely needs to bind a privileged port.
  • Set runAsNonRoot: true and define readOnlyRootFilesystem: true wherever the application supports it.
  • Pin procMount to the default value rather than Unmasked.
  • Restrict volume types to the approved set and avoid hostNetwork, hostPID, or hostPort unless the workload is explicitly exempted as infrastructure.
  • Limit sysctls to the documented safe list rather than allowing arbitrary kernel parameter overrides.

The Restricted profile requires dropping all capabilities and forbids the Unconfined seccomp profile, two of the most commonly missed fields when teams migrate existing workloads, according to the Kubernetes Pod Security Standards documentation. Baking these checks into CI, rather than catching them at admission time, turns a blocked deployment into a quick pull request comment instead.

Where PSS Falls Short and When to Add a Policy Engine

PSA is deliberately narrow. It validates against three fixed profiles and cannot mutate a pod spec to fix it, so any workload that depended on PodSecurityPolicy quietly injecting defaults needs either a manifest rewrite or a Mutating Admission Webhook to do that work going forward.

  • PSA has no mutation capability, so defaults like forcing runAsNonRoot onto a pod that forgot to set it must come from a webhook, not PSA itself.
  • The three PSS levels are opinionated and fixed. Fine-grained rules, custom field restrictions, or organization-specific exceptions need a dedicated policy engine.
  • Tools like Kyverno, OPA Gatekeeper, or Kubewarden fill that gap, at the cost of added operational complexity and another system to trust and maintain. Our guide to Kyverno admission rules covers how to layer that on top of PSA rather than replace it.

Patterns We Use for CI-Gated Pod Security

Catching violations in a pull request beats catching them at the admission controller, every time. Our typical CI gate runs a manifest linter against the target PSS profile before merge, checks securityContext fields explicitly, and fails the build on any Restricted violation rather than waiting for a cluster rejection.

We have also found AI-assisted manifest review genuinely useful here: a well-structured prompt can flag a missing seccompProfile or an unnecessary capability faster than a human reviewer skimming a long YAML file, which is the workflow we document in our AI manifest auditing guide. Pair that with RBAC rules that limit who can change the PSS labels on a namespace, since a loosened label is a much quieter failure mode than a rejected deployment.

Reading audit logs after the fact tells you what almost went wrong. Gating in CI stops it from becoming an incident in the first place.

— James

Getting the Rollout Order Right

Pod Security Standards are one control among several, not a complete security posture on their own. RBAC, network policy, and runtime security tooling still carry weight PSS was never built to cover, and treating it as the whole job is how gaps get missed.

We would rather see a namespace sit in audit mode for a month longer than see a rushed enforce rollout take down a batch job nobody tested. Slow, observable rollouts beat brittle hardening that looks secure on paper and breaks the first time an edge case shows up.

— James

How We Can Help You Roll This Out Faster

Working through namespace-by-namespace PSS migration on top of everything else on your plate is exactly the kind of work our Kubernetes Health Check was built for: a focused, $300 one-off review that looks at your current admission configuration, flags workloads that will break under Restricted, and hands you a concrete rollout plan instead of a generic audit report.

Devopsaitoolkit

If your infrastructure spans Terraform-managed clusters too, a Terraform / IaC Audit covers the broader configuration surface as a one-off review, and these reviews are informed by prompt libraries and manifest-checking workflows used in practice.

  • Kubernetes Health Check: a one-off review of your PSA configuration and workload readiness.
  • Terraform / IaC Audit: a one-off review of the infrastructure code feeding your clusters.
  • Hourly consulting available for teams that need hands-on help finishing a migration already in progress.

Book a review through our work-with-me page and we will tell you exactly where your namespaces stand before you flip a single label to enforce.

FAQ

What are the four C’s of Kubernetes security?

The four C’s are a commonly used mental model for layered security: Cloud, Cluster, Container, and Code, working from the infrastructure layer inward to the application itself. Pod Security Standards operate mainly at the Cluster and Container layers, setting rules for how pods can be configured, while code-level and cloud-level controls need separate tooling.

What does “restricted” mean in the context of Pod Security Standards?

Restricted is the strictest of the three Pod Security Standards profiles, and it requires pods to run as a non-root user, drop all Linux capabilities, and avoid the Unconfined seccomp profile, according to the Kubernetes Pod Security Standards. It is intended for workloads handling sensitive data or exposed to untrusted traffic, where a compromised container should have as little leverage as possible.

What is a security pod?

There is no distinct Kubernetes object called a “security pod.” The term usually refers to a regular pod configured under a hardened securityContext, following the Baseline or Restricted Pod Security Standards profile rather than running with default, unrestricted settings.

What are the best security practices for Kubernetes?

Layering controls works better than relying on any single mechanism: apply Pod Security Standards at the namespace level, pair them with network policies and tight RBAC, and keep runtime security monitoring in place for behavior PSS cannot see. Least-privilege thinking extends beyond pod configuration into service accounts and network access, a principle covered well in this explanation of least privilege access.

How do I migrate from PodSecurityPolicy without breaking workloads?

Map each PodSecurityPolicy field to its Pod Security Standards equivalent first, paying close attention to any field PSP used to mutate automatically, since PSA cannot do that. The official migration guide recommends testing with audit and warn mode before enforcing, which gives you a safe window to fix workloads that depended on PSP’s old mutating behavior.

Newsletter

Free: the DevOps AI Incident-Triage Cheat Sheet

Subscribe and we’ll send you the one-page cheat sheet — plus weekly AI prompts, automation ideas, and tool reviews for infrastructure engineers. One email a week. No spam, unsubscribe anytime.

  • AI Incident-Triage Cheat Sheet (PDF)
  • Access to 2,778 DevOps AI prompts
  • One practical workflow email per week
Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.