To run a container with a read-only root filesystem, set the read-only option in Docker or readOnlyRootFilesystem in a Kubernetes security context, then give the app explicit writable locations such as a temporary filesystem or a volume. Attackers lose an easy place to drop tools, and accidental writes show up quickly.
What it does
The Docker run reference says the read-only option mounts the container's root filesystem as read only, prohibiting writes to locations other than the specified volumes. Kubernetes has the same idea. Its security context documentation says readOnlyRootFilesystem mounts the container's root filesystem as read-only.
This matters because many post-exploit steps need to write: downloading a second-stage tool, editing a config file, replacing a binary, or leaving a web shell in the app directory. A read-only root makes each of those fail unless the attacker finds a writable path.
It is not a cure. A process can still run code in memory, use writable mounts, and talk to the network. Think of it as removing one convenient option.
Turn it on in Docker
docker run --read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m myimage
The tmpfs option creates a memory-backed mount. The Docker reference shows the same style of options as the Linux mount command, such as rw, noexec, nosuid and a size. Using noexec on a scratch path also makes it harder to run anything dropped there.
If the app needs persistent writes, add a named volume or bind mount for that one path and nothing else. The OWASP cheat sheet suggests mounting volumes read-only too when a container only reads them.
Turn it on in Kubernetes
securityContext:
readOnlyRootFilesystem: true
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
An emptyDir mounted at the temporary path gives the app a writable scratch space while the rest of the image stays fixed. Add more mounts only for paths you can name.
Find out what the app writes
Apps fail in predictable places. Typical ones are:
- A temp directory the framework expects
- A pid file or socket under a run directory
- A cache folder in the home directory
- Log files written to disk instead of standard output
- A package manager or compiler cache at start
The fastest way to find them is to start the container read-only in a test environment and read the error messages. Each "read-only file system" error names a path. Decide whether to mount it, redirect it with an environment variable, or fix the app to stop writing there.
Rollout order that keeps people calm
- Enable it in a development namespace first.
- Run the normal test suite and a few real requests.
- Collect the list of write paths and add mounts.
- Enable it in staging, then production.
- Add a policy check so new workloads cannot skip it.
Combine it with running as a non-root user, dropped capabilities and the no-new-privileges option. See the non-root guide.
Safeguard's policies, gates and guardrails can be used to check workload settings against your own rules as part of review. Start by seeing what is in your images with a free scan.
Quick check
Will a read-only root break my database container?
Only if the data directory is on the root filesystem. Put the data directory on a volume and the rest can stay read-only.
Is a temporary filesystem safe to leave writable?
It is a smaller risk than a writable root, especially with noexec and nosuid. Keep the size limited and do not store anything there you need to keep.
Sources
- Docker container run reference
- Kubernetes: configure a security context
- OWASP Docker Security Cheat Sheet
Last reviewed: 19 October 2026.