Safeguard
Security Guides•

How to run Kubernetes containers with a read-only root filesystem

Set readOnlyRootFilesystem true and give the app writable paths through emptyDir. Steps, common failures and what this setting does and does not stop.

Safeguard Security Team
3 min read

Set readOnlyRootFilesystem to true on each container and mount small writable volumes, such as emptyDir, for the paths the application must write. An attacker who gets code execution then cannot easily modify binaries or drop tools into the image filesystem. It does not stop everything, so use it with the other hardening settings.

What the setting does

The Kubernetes security context docs describe readOnlyRootFilesystem as mounting the container's root filesystem as read-only, with any writable paths provided by volumes. It is a container level field.

containers:
- name: app
  image: registry.example.com/app:1.4.2
  securityContext:
    readOnlyRootFilesystem: true
    allowPrivilegeEscalation: false
  volumeMounts:
  - name: tmp
    mountPath: /tmp
  - name: cache
    mountPath: /var/cache/app
volumes:
- name: tmp
  emptyDir: {}
- name: cache
  emptyDir: {}

The emptyDir volume is one of the volume types the Restricted standard allows.

Steps to roll it out

  1. Turn it on in a test environment.
  2. Run the app and its tests. Read the logs for errors such as read-only file system.
  3. Note each path that fails. Typical ones are tmp, a cache folder, a PID file location and a framework working directory.
  4. Add an emptyDir mount for each path.
  5. Repeat until clean, then ship.

Many images write to a handful of places, so the list is usually short. Where configuration files must be generated at start, generate them in a writable mount or at image build time.

Common failures

Application cannot start because it writes a lock or PID file: mount a writable directory there and, if the app allows, configure its path.

Package manager or updater runs at start: this is an image smell. Bake dependencies into the image at build time.

Language runtimes writing compiled caches: point the cache directory to an emptyDir through an environment variable, if the runtime supports it.

Writable data that must survive restarts: use a PersistentVolumeClaim, not the container filesystem.

What it does and does not do

It makes tampering with the image filesystem harder, and it makes unexpected writes visible as failures. It does not block writes to the mounted volumes, memory only attacks, or network activity. An attacker can still run code that is already in the image or loaded in memory, and can write to emptyDir paths.

That is why it belongs next to non-root users, dropped capabilities and a seccomp profile. The whole set is in the securityContext checklist.

A smaller image with fewer tools in it also helps, since there is less to run once an attacker is inside.

Order of rollout

Start with stateless web services, which usually need only tmp. Move to workers and batch jobs next. Leave databases and legacy applications for last, since they write in many places. For each service, record the writable paths you added so the next engineer knows why they exist.

Quick check

Does read-only root block writes to mounted volumes?

No. Only the container's own root filesystem is read-only. Volumes keep their own mode.

Is this required by Pod Security Standards?

The fetched pages do not list it among the Restricted requirements, so treat it as extra hardening.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.