Workload Security
Read-only Root Filesystem
Preventing writes to the container filesystem to blunt tampering and persistence.
readOnlyRootFilesystem: true mounts the container’s own filesystem read-only. The process can read everything the image contains but modify none of it.
This removes a whole class of post-compromise moves. An attacker who gets code execution in the container can no longer:
- drop tools like a crypto miner or reverse shell binary onto disk
- overwrite the application’s binaries, scripts, or configuration
- persist anything across a container restart
It also enforces a good architectural habit: containers should be immutable artifacts, with all real state in volumes or external stores.
See It Work
apiVersion: v1
kind: Pod
metadata:
name: rofs
namespace: lab
spec:
containers:
- name: app
image: busybox:1.36
command: ["sleep", "3600"]
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
kubectl apply -f rofs.yaml
kubectl exec -n lab rofs -- touch /evil
Expected output:
touch: /evil: Read-only file system
command terminated with exit code 1
Even /tmp is read-only now:
kubectl exec -n lab rofs -- touch /tmp/scratch
touch: /tmp/scratch: Read-only file system
Give The App Its Writable Paths Back
Most applications write somewhere: temp files, caches, pid files, logs. The pattern is a read-only root plus explicit emptyDir mounts at exactly those paths:
apiVersion: v1
kind: Pod
metadata:
name: rofs-tmp
namespace: lab
spec:
containers:
- name: app
image: busybox:1.36
command: ["sleep", "3600"]
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
kubectl apply -f rofs-tmp.yaml
kubectl exec -n lab rofs-tmp -- touch /tmp/scratch && echo write ok
Expected output:
write ok
An emptyDir lives only as long as the pod, so this keeps the immutability story intact: nothing written there survives rescheduling.
Finding The Paths An App Needs
Turn the setting on in a dev environment and read the crash. Errors like EROFS: read-only file system, open '/var/cache/app/...' tell you exactly which paths need an emptyDir. Common ones:
/tmp: almost everything/var/runor/run: pid files and sockets/var/cache/<app>and/var/log/<app>: servers like nginx$HOME/.cache: runtimes and CLIs
Two or three mounts usually cover it.
Clean Up
kubectl delete pod rofs rofs-tmp -n lab
Practical Guidance
- Make
readOnlyRootFilesystem: truethe default in your pod templates and treat writable roots as exceptions with a reason. - Mount
emptyDirvolumes at the specific paths an app writes, withsizeLimitset so a compromised pod cannot fill the node disk. - Ship logs to stdout/stderr instead of files, and state to volumes or external stores, and then the read-only root costs nothing.
- Note that the
restrictedPod Security Standard does not require this field, so add it to your admission policy explicitly if you want it enforced. - Combine with
runAsNonRootand dropped capabilities; each control blocks the workarounds for the others.