Kubernetes
7 min readMay 30, 2026

Kubernetes PodSecurityContext vs SecurityContext: Which One Applies

Both PodSecurityContext and SecurityContext control Linux security settings in Kubernetes — but they apply at different scopes. Get the scope wrong and your security settings either silently don't apply or get overridden by something you didn't expect.

AJ
Ajeet Yadav
Platform & Cloud Engineer
Kubernetes PodSecurityContext vs SecurityContext: Which One Applies

Kubernetes has two places where you can set security settings for a workload: spec.securityContext (the pod-level PodSecurityContext) and spec.containers[*].securityContext (the container-level SecurityContext). They share similar names, some overlapping fields, and the same YAML path prefix — which is why they get conflated.

The rule is simple once you know it: pod-level settings are defaults for all containers in the pod. Container-level settings override pod-level for that specific container. Some fields only exist at one level.

The Pod-Level: PodSecurityContext

spec.securityContext applies to all containers in the pod, including init containers.

yaml
1spec:
2  securityContext:
3    runAsUser: 1000
4    runAsGroup: 3000
5    fsGroup: 2000
6    runAsNonRoot: true
7    seccompProfile:
8      type: RuntimeDefault

The fields that only exist at the pod level (you can't set them per-container):

fsGroup — the GID assigned to all mounted volumes. The kubelet changes volume ownership to match this GID before mounting, and all processes in the pod can read/write those volumes. This can't be set per-container because volume ownership is a pod-level concept.

supplementalGroups — adds extra GIDs to all containers in the pod. Useful when processes need access to specific resources (e.g., a shared group for GPU access).

sysctls — kernel parameter settings that apply to the pod's network namespace. Safe sysctls are allowed by default; unsafe ones require explicit opt-in at the node level.

yaml
spec:
  securityContext:
    sysctls:
      - name: net.core.somaxconn
        value: "1024"

The Container Level: SecurityContext

spec.containers[*].securityContext applies to a single container. This is where you control container-specific privileges.

yaml
1spec:
2  containers:
3    - name: app
4      securityContext:
5        allowPrivilegeEscalation: false
6        readOnlyRootFilesystem: true
7        runAsNonRoot: true
8        capabilities:
9          drop: ["ALL"]
10          add: ["NET_BIND_SERVICE"]

Fields that only exist at the container level:

allowPrivilegeEscalation — prevents setuid binaries and sudo from gaining extra privileges. Defaults to true, which means any setuid binary in your container image can escalate. Set this to false on all containers unless you know you need it.

readOnlyRootFilesystem — mounts the container's root filesystem as read-only. Forces you to explicitly declare writable paths via emptyDir or tmpfs volumes. This is one of the best mitigations against container escape and post-compromise persistence.

capabilities — fine-grained Linux capabilities. The right pattern is drop: ["ALL"] then add back only what you need (usually nothing, occasionally NET_BIND_SERVICE for ports below 1024).

privileged — full host access. Never use this in production. If a container needs it, that's an architectural problem.

procMount — controls /proc visibility. Default (masked) is correct for almost everything.

What Happens When Both Are Set?

For fields that exist in both PodSecurityContext and container SecurityContextrunAsUser, runAsGroup, runAsNonRoot, seccompProfile, seLinuxOptions — the container-level value wins for that container.

yaml
1spec:
2  securityContext:
3    runAsUser: 1000    # applies to all containers by default
4  containers:
5    - name: app
6      securityContext:
7        runAsUser: 2000  # overrides pod-level for this container only
8    - name: sidecar
9      # no securityContext — inherits runAsUser: 1000 from pod

In this example, app runs as UID 2000. sidecar runs as UID 1000.

This override behavior is per-field. If you set runAsUser at the container level, that overrides the pod-level runAsUser — but fsGroup is unaffected since it doesn't exist at the container level.

A Hardened Baseline

Here's the security context pattern I apply to most production workloads:

yaml
1spec:
2  securityContext:
3    runAsNonRoot: true
4    runAsUser: 1000
5    runAsGroup: 1000
6    fsGroup: 1000
7    seccompProfile:
8      type: RuntimeDefault
9  containers:
10    - name: app
11      image: my-app:latest
12      securityContext:
13        allowPrivilegeEscalation: false
14        readOnlyRootFilesystem: true
15        capabilities:
16          drop: ["ALL"]
17      volumeMounts:
18        - name: tmp
19          mountPath: /tmp
20        - name: cache
21          mountPath: /app/cache
22  volumes:
23    - name: tmp
24      emptyDir: {}
25    - name: cache
26      emptyDir: {}

The readOnlyRootFilesystem: true typically breaks applications that write to their own directory. You fix this by explicitly mounting emptyDir volumes at the paths the app needs to write to. It's a one-time audit of your container's write patterns — worth doing.

Which Fields Live Where?

FieldPodSecurityContextSecurityContext
runAsUser✓ (overrides pod)
runAsGroup✓ (overrides pod)
runAsNonRoot✓ (overrides pod)
fsGroup✓ only
supplementalGroups✓ only
sysctls✓ only
seccompProfile✓ (overrides pod)
seLinuxOptions✓ (overrides pod)
allowPrivilegeEscalation✓ only
readOnlyRootFilesystem✓ only
capabilities✓ only
privileged✓ only
procMount✓ only

Validating Your Settings

After applying security contexts, verify they took effect:

bash
1# Check what user a container is actually running as
2kubectl exec my-pod -c app -- id
3# uid=1000(app) gid=1000(app) groups=1000(app),2000
4
5# Confirm no privilege escalation
6kubectl exec my-pod -c app -- cat /proc/1/status | grep -E "^(Cap|No)"
7
8# Check if filesystem is read-only
9kubectl exec my-pod -c app -- touch /test-write 2>&1
10# touch: /test-write: Read-only file system

If you're using Pod Security Admission, the restricted policy enforces most of these settings and will reject pods that don't comply. For existing clusters, use audit or warn mode first before enforcing — it'll tell you which pods would fail without breaking anything.

Related Topics

Kubernetes
Security
PodSecurityContext
SecurityContext
Linux
DevOps

Found this useful? Share it.

Practice this

Read Next