Skip to content

Pod Security Standards

First PublishedLast UpdatedByAtif Alam

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.

ProfileIntent
PrivilegedUnrestricted; use only for system namespaces and rare break-glass
BaselinePrevents known high-risk footguns (host namespaces, hostPath, etc.)
RestrictedHardened 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.

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.

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.

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.

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 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.

You typically can still

  • Run read-only commands: kubectl get, list, watch on most resources.
  • Apply changes that do not create a violating pod spec.
  • kubectl exec into 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: true

Preview without applying: use server-side validation so the API runs admission without persisting the change:

Terminal window
kubectl apply -f workload.yaml --dry-run=server

See 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.

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.

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: v1
kind: Namespace
metadata:
name: app-team-a
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: baseline
pod-security.kubernetes.io/warn: restricted
SituationSuggested starting point
New cluster or new app namespace you ownBaseline or Restricted after an audit pass
Legacy apps, vendor charts, unknown UIDsBaseline 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.

  1. Audit on representative namespaces — collect violations in audit logs without blocking.
  2. Warn — surface warnings to developers and CI; fix Helm charts and manifests.
  3. 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.

  • runAsNonRoot: true and explicit runAsUser in an allowed range (or use the namespace’s user constraints as documented upstream).
  • Drop Linux capabilities; avoid CAP_SYS_ADMIN.
  • allowPrivilegeEscalation: false.
  • readOnlyRootFilesystem: true where 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"]