Pod Security Standards
Pod Security Standards (PSS) are three namespace-level policy levels — Privileged, Baseline, and Restricted. Pod Security Admission (PSA), built in from Kubernetes 1.25+, evaluates pod-shaped API data at admission time using labels on the namespace. For the full rule matrix by version, see the official Pod Security Standards documentation.
Profiles (short)
Section titled “Profiles (short)”| Profile | Intent |
|---|---|
| Privileged | Unrestricted; use only for system namespaces and rare break-glass |
| Baseline | Prevents known high-risk footguns (host namespaces, hostPath, etc.) |
| Restricted | Hardened defaults: non-root, dropped capabilities, seccomp, volume restrictions |
Vocabulary: the profile named Privileged is a label value on the namespace — it means “PSS does not restrict pod specs in this namespace.” That is not the same thing as securityContext.privileged: true on a container. The field privileged: true is a Linux escape hatch into full host-like capabilities; Baseline and Restricted disallow it for workloads that must stay within the profile. Do not conflate the word “privileged” in the profile name with that container field.
How enforcement works
Section titled “How enforcement works”Namespace labels (see Namespace labels) tell the API server which Pod Security profile applies. On each matching write, Pod Security Admission allows or denies the request before it is stored in etcd.
What is checked
Section titled “What is checked”PSA evaluates Pod objects and the pod template inside these workload kinds:
Deployment, StatefulSet, DaemonSet, ReplicaSet, Job, CronJob.
You can hit a policy error by changing a Deployment spec even if you never keep a standalone Pod manifest in git.
When it runs
Section titled “When it runs”On create and on update when pod or pod-template spec changes. Writes that never touch pod shape (for example, many ConfigMap edits alone) are not evaluated by PSA.
Already-running pods
Section titled “Already-running pods”Stricter labels do not rewrite or evict pods that are already running. Problems show up when new Pod objects are admitted—after a delete, scale, rollout, or controller recreate. A pod can keep running while still being out of date with the new policy.
RBAC, network security, and scope
Section titled “RBAC, network security, and scope”- RBAC answers: is this caller allowed to write the Pod or controller?
- PSS answers: is this pod spec allowed in this namespace?
Both must allow the change.
PSS only constrains fields on the pod spec. It does not replace NetworkPolicy, secret handling, image provenance, or mesh rules.
The legacy PodSecurityPolicy (PSP) admission plugin is removed from Kubernetes; Pod Security Admission + PSS is the supported built-in path on current releases.
Windows: PSS text targets Linux pod semantics. Windows pods follow a different security model; treat mixed-OS clusters as a separate topic.
What you can and cannot do
Section titled “What you can and cannot do”You typically can still
- Run read-only commands:
kubectl get,list,watchon most resources. - Apply changes that do not create a violating pod spec.
kubectl execinto a pod that is already running (if RBAC allows), even after labels change — admission already passed for that pod object. “Still runs” is not the same as “meets the new policy”; restarts or reschedules can still fail later.
Under enforce, you cannot
- Create or update a Pod or workload pod template whose spec violates the enforced profile for that namespace. The API server responds with a forbidden style error (often HTTP 403); exact wording varies by version and client.
Concrete rejection (Restricted namespace): a Pod with securityContext.privileged: true or a disallowed hostPath volume is rejected. Example fragment that will fail admission when enforce: restricted:
spec: containers: - name: app image: nginx:latest securityContext: privileged: truePreview without applying: use server-side validation so the API runs admission without persisting the change:
kubectl apply -f workload.yaml --dry-run=serverSee also Admission controllers for how admission fits in the request path and for CI-time checks.
Where warn shows: when warn is set and the spec would violate that level, clients often see Warning headers or messages on stderr (for example with kubectl apply) even though the request is not blocked. Use that signal in CI and local workflows before you flip enforce.
Symptoms when something else creates pods: a Deployment may exist while ReplicaSets or Pods fail. Use kubectl describe deployment, kubectl describe replicaset, and Events (kubectl get events) to see admission failures. Helm installs and hooks can fail the same way when the chart requests a forbidden spec.
kubectl debug and ephemeral containers
Section titled “kubectl debug and ephemeral containers”Restricted namespaces often block ephemeral containers and kubectl debug defaults that ask for extra capabilities or relaxed profiles. Prefer a dedicated debug namespace labeled to Baseline (or another org-approved level) for interactive debugging, instead of widening production namespaces to Privileged.
Namespace labels
Section titled “Namespace labels”Admission reads labels such as pod-security.kubernetes.io/enforce, pod-security.kubernetes.io/audit, and pod-security.kubernetes.io/warn with values privileged, baseline, or restricted.
You can set different levels per mode. For example, enforce the strictest level you intend to ship while audit at a looser level to collect a broader set of “would have failed Baseline” signals in audit logs without blocking — tune to your noise tolerance.
Example (restricted in enforce, baseline in audit for visibility, warn aligned with enforce):
apiVersion: v1kind: Namespacemetadata: name: app-team-a labels: pod-security.kubernetes.io/enforce: restricted pod-security.kubernetes.io/audit: baseline pod-security.kubernetes.io/warn: restrictedChoosing a profile
Section titled “Choosing a profile”| Situation | Suggested starting point |
|---|---|
| New cluster or new app namespace you own | Baseline or Restricted after an audit pass |
| Legacy apps, vendor charts, unknown UIDs | Baseline with audit / warn first, then tighten |
| System components (CNI, storage daemons, node agents) | Privileged profile only where the vendor requires it — keep scope small |
PSS is per namespace. It is normal for kube-system (or similar) to differ from application namespaces.
Recommended practices
Section titled “Recommended practices”- Audit on representative namespaces — collect violations in audit logs without blocking.
- Warn — surface warnings to developers and CI; fix Helm charts and manifests.
- Enforce — block non-compliant creates/updates; keep a documented exception process (ticket, owning team, expiry date) for namespaces that must stay looser for a time.
Exceptions — two layers
- Process: track why a namespace is not yet at target policy and when it will be revisited.
- Mechanics: PSA exemptions are configured at the cluster (API server configuration) by platform admins — for example exempting specific namespaces, workloads, or identities. There is no standard “exempt just this Pod” flag next to the Pod spec for developers. Day-to-day relief is usually fix the manifest, move the workload to an appropriate namespace, or request a label or exemption change through your platform team — not a silent per-pod bypass.
CI: run kubectl apply --dry-run=server and/or policy-as-code in pipelines (Policy as code is a useful companion).
Anti-pattern: labeling every namespace Privileged to avoid triage — you lose the safety net entirely.
Optional: pin behavior with pod-security.kubernetes.io/enforce-version (and the matching audit-version / warn-version labels) if you need a specific Kubernetes minor’s policy definition; see the upstream Pod Security Admission documentation.
Common app changes for Restricted
Section titled “Common app changes for Restricted”runAsNonRoot: trueand explicitrunAsUserin an allowed range (or use the namespace’s user constraints as documented upstream).- Drop Linux capabilities; avoid
CAP_SYS_ADMIN. allowPrivilegeEscalation: false.readOnlyRootFilesystem: truewhere the app supports it.- seccomp profile
RuntimeDefault(or a workload-specific profile). - Replace hostPath and hostNetwork patterns with supported volume types and in-cluster services.
Minimal securityContext shape (illustrative): before, a container might omit security context; after, tighten pod or container scope:
# After (sketch — merge into your pod spec)spec: securityContext: runAsNonRoot: true runAsUser: 1000 seccompProfile: type: RuntimeDefault containers: - name: app image: your-registry/app:1.2.3 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"]Official reference
Section titled “Official reference”- Pod Security Standards — profile definitions and versioned requirements.
- Pod Security Admission — labels, modes, and version pinning.
Related
Section titled “Related”- Admission controllers — Validating/mutating admission alongside PSS.
- Network policies — Traffic allowlists; complements PSS but does not replace it.
- Multi-tenancy and policy — Broader tenant isolation model.
- Architecture review answers — Prompts this page deepens.