Read-Only Filesystems
The simplest, most overlooked way to make a container harder to tamper with: tell it, from the moment it starts, that it is not allowed to change itself at all.
By default, a running Docker container behaves like a normal computer: its filesystem is fully writable, and any process inside it can create files, modify existing ones, or overwrite system binaries without Docker raising any objection. For most everyday development this is convenient and unremarkable. But it also means that if an attacker ever manages to run malicious code inside that container, the container’s own filesystem gives them a place to plant tools, rewrite configuration, or install persistence — exactly the way a normal, unprotected computer would. A read-only filesystem removes that option at the root: it tells Docker that the container’s filesystem should reject writes entirely, forcing anything that genuinely needs to write somewhere to do so through a small, deliberately chosen exception. This guide covers what a read-only filesystem actually changes, how Docker enforces it, and how to design a container that stays useful while being locked down this way.
ACore Concepts
What “read-only filesystem” means here
Running a container with its filesystem set to read-only means the container’s own root filesystem — the layer built from its image — cannot be written to at all while it is running. Any attempt by a process inside the container to create, modify, or delete a file on that root filesystem is rejected by the operating system, exactly as if the container’s disk were a sealed, non-rewritable medium.
What stays writable regardless
A read-only root filesystem does not mean a container can never write anything, anywhere. Volumes and bind mounts attached to the container remain writable unless separately configured otherwise, and Docker provides a specific mechanism — temporary, in-memory filesystem mounts — for giving a container small, deliberately chosen writable locations even while its root filesystem stays locked.
Why this is fundamentally a security and reliability feature
Making the root filesystem read-only is less about performance and more about constraint: it removes an entire category of things that can go wrong or be exploited, by simply making them impossible rather than merely discouraged. An application cannot accidentally corrupt its own installed files, and an attacker who gains code execution inside the container cannot use the container’s own disk to install anything persistent.
Think of a container image like a printed textbook rather than a notebook. A notebook invites anyone holding it to scribble in the margins, tear out pages, or add sticky notes anywhere. A printed textbook cannot be altered that way — if you genuinely need to jot something down, you use a separate notepad kept alongside it. A read-only filesystem turns the container’s root into that sealed textbook, while volumes and tmpfs mounts are the deliberately provided notepad for anything that truly needs to be written down.
BArchitecture & Components
What gets locked
The filesystem assembled from the image’s layers, which becomes the part of the container that is made read-only when this feature is enabled.
The switch itself
The setting applied when starting a container that tells Docker to mount the root filesystem without write permission for the container’s own processes.
An in-memory exception
A temporary, memory-backed writable location that can be mounted at a specific path (such as a scratch or cache directory) even while the rest of the filesystem stays read-only.
Persistent, deliberate exceptions
Storage explicitly attached to the container that remains writable regardless of the root filesystem setting, used for data that genuinely needs to persist.
What actually needs writing
The specific directories a given application legitimately writes to — logs, temporary files, a cache — which need to be identified before a read-only setup will work without crashing.
Enforcement at a larger scale
A setting in a container orchestrator that can require every container in a deployment to run with a read-only root filesystem, turning this from a per-container choice into an organization-wide standard.
The overall shape is simple: one setting locks the entire root filesystem, and two separate, deliberately chosen mechanisms — tmpfs mounts and volumes — provide exactly the writable space an application actually needs, and nothing more.
CInternal Working
When a container is started with its root filesystem set to read-only, Docker mounts that filesystem without write permission granted to the container’s processes. Any system call attempting to write to a path on that filesystem — creating a file, appending to one, deleting one — is rejected by the underlying operating system, and the process making that attempt receives a permission error exactly as it would trying to write to any other read-only disk.
Why applications often fail immediately without adjustment
Many applications write somewhere by default without anyone thinking about it explicitly — a temporary file, a lock file, a cache directory, a log file written directly to disk instead of standard output. The moment the root filesystem becomes read-only, every one of those unnoticed writes starts failing, which is why simply flipping this setting on an unmodified application frequently causes it to crash or behave incorrectly almost immediately.
How tmpfs mounts resolve this cleanly
A tmpfs mount is layered on top of a specific path inside the container, creating a small island of writable, memory-backed storage at exactly that location, while everything else on the root filesystem remains locked. Because it lives in memory rather than on the container’s actual filesystem layer, anything written there disappears when the container stops — which is exactly the right behavior for genuinely temporary data like scratch files or an in-process cache, and exactly the wrong choice for anything that needs to survive a restart.
Read-only affects the container’s own root filesystem specifically. It has no effect on volumes or bind mounts attached to the container, which is exactly why those remain the correct place for any data that genuinely needs to be written and kept.
DData Flow & Lifecycle
flowchart TD
A["Container started with read-only root filesystem"] --> B["Process attempts to write a file"]
B --> C{"What path is being written to?"}
C -->|"Root filesystem path, no mount"| D["Write rejected: permission error"]
C -->|"Path with tmpfs mounted"| E["Write succeeds: stored in memory"]
C -->|"Path with volume or bind mount"| F["Write succeeds: stored persistently"]
E --> G["Container stops"]
G --> H["tmpfs contents discarded"]
F --> I["Container stops"]
I --> J["Volume contents remain for next container"]
Fig. 1 — The same write attempt succeeds or fails entirely depending on whether the target path sits on the locked root filesystem, a tmpfs mount, or a persistent volume.
The practical lesson in this flow is that “read-only container” is really shorthand for “read-only root filesystem, plus exactly the writable exceptions you deliberately chose.” Getting a real application running this way is mostly the work of identifying every path it actually needs to write to, and then routing each one to either a tmpfs mount, if the data is disposable, or a proper volume, if it needs to persist.
A typical web application example
A web server might need a writable location for temporary upload processing and another for its own cache, both of which are fine to lose on restart, while its actual uploaded files and logs, if they need to persist, are routed to a mounted volume instead — resulting in a container whose core application code and configuration remain completely locked, while its genuine operational needs are still met.
EAdvantages, Disadvantages & Trade-offs
Strengths
- Blocks an attacker who gains code execution from writing malicious files, tools, or persistence to the container’s own disk
- Prevents an application from accidentally corrupting its own installed files or configuration at runtime
- Forces explicit, intentional design of what a container is actually allowed to write, improving overall clarity of the container’s behavior
Limitations
- Requires upfront work to identify every path an application genuinely writes to, which can involve some trial and error for unfamiliar applications
- Some third-party or legacy applications assume a fully writable filesystem and may need extra configuration, patches, or an alternative image to work correctly
- tmpfs-backed writable paths consume memory rather than disk, which matters for applications that write large amounts of temporary data
The trade-off in one sentence: a read-only filesystem trades a modest amount of upfront setup effort for a meaningful, durable reduction in what can go wrong or be exploited for the entire lifetime of every container run from that image.
FSecurity
A large share of real-world container compromises follow a similar shape: an attacker finds a vulnerability that lets them execute code inside a running container, and then they use the container’s own writable filesystem to download additional tools, modify application code, or establish some form of persistence so their access survives a restart. A read-only root filesystem removes that entire step from the attacker’s playbook — even with code execution, there is simply nowhere on the container’s own disk left to write anything.
A read-only filesystem does not prevent an attacker from reading existing files, exfiltrating data already present, or exploiting anything reachable over the network from inside the container. It should be treated as one strong layer among several — alongside least-privilege user accounts, minimal installed tools, and network restrictions — not as a single, complete defense on its own.
Security benchmarks and hardening guides for containerized environments commonly recommend read-only root filesystems as a baseline expectation specifically because the cost is mostly one-time setup work, while the protection it provides continues for the entire operational life of every container built from that image, with no ongoing maintenance burden once the writable exceptions have been correctly identified.
GDesign Patterns & Anti-patterns
Pattern
Auditing an application’s real write behavior first — running it normally and observing exactly what it writes to and where — before enabling a read-only root filesystem.
Why it works
It replaces guesswork with an accurate list of the specific paths that genuinely need a tmpfs mount or a volume, avoiding both under-provisioning (crashes) and over-provisioning (unnecessary writable exceptions that weaken the protection).
Pattern
Routing genuinely temporary data — caches, scratch space, lock files — to tmpfs mounts, and reserving actual volumes only for data that must survive a container restart.
Why it works
It keeps the set of truly persistent, meaningfully important writable locations small and easy to reason about, while still giving the application everywhere it legitimately needs to write for short-lived operations.
Anti-pattern
Enabling a read-only root filesystem on an existing application without first checking what it actually writes to, and only discovering the missing writable paths through repeated crashes in production.
Why it causes problems
It turns a security improvement into an unplanned outage, and the resulting scramble to identify missing write paths under pressure often leads to overly broad, hastily added exceptions that undermine the original goal.
Anti-pattern
Mounting an overly broad, high-level directory as writable (such as the application’s entire installation directory) just to make crashes stop, instead of narrowing the exception down to the specific paths that truly need it.
Why it causes problems
It quietly recreates most of the original attack surface a read-only filesystem was meant to remove, since a wide writable directory can still be used to plant files or modify application code.
HBest Practices & Common Mistakes
Identify write paths before enabling read-only
Test the application normally first, watching for every file or directory it writes to, so the writable exceptions can be set up correctly from the start.
Prefer tmpfs for disposable data
Anything that does not need to survive a restart belongs in a tmpfs mount rather than a persistent volume, keeping the persistent footprint small.
Test the read-only configuration under real load
Some write paths only appear under specific conditions — error handling, edge cases, less common code paths — so testing beyond the happy path matters.
Assuming every application is compatible without changes
Some applications or third-party tools genuinely require a writable location for their own internals and may need explicit configuration to point that elsewhere.
Treating this as a substitute for other hardening steps
A read-only filesystem is one layer of protection, not a replacement for running as a non-root user, limiting capabilities, or restricting network access.
Forgetting logging behavior
An application configured to write logs directly to a file on the root filesystem will fail under a read-only setup — redirecting logs to standard output, as most containerized applications should anyway, sidesteps this entirely.
IReal-World & Industry Examples
Container security benchmarks
Widely used container hardening guidelines, including the CIS Docker Benchmark, specifically recommend running containers with a read-only root filesystem wherever the application allows it, listing it as a standard baseline hardening step.
Kubernetes security contexts
Kubernetes exposes a direct setting for requiring a read-only root filesystem on a pod’s containers, and organizations running security-sensitive workloads commonly enforce it across their clusters as a required policy rather than an optional choice.
Minimal and distroless base images
Teams building especially hardened images often pair a read-only root filesystem with a minimal base image containing as few writable-by-default tools and binaries as possible, compounding the security benefit of both choices together.
Regulated and high-security industries
Organizations in finance and other compliance-heavy sectors frequently adopt read-only root filesystems as part of a broader effort to demonstrate that running containers cannot be silently modified after deployment, supporting audit and compliance requirements around software integrity.
JFrequently Asked Questions
KSummary and Key Takeaways
The essentials, in one place
- A read-only filesystem locks the container’s root, rejecting any write attempt to the filesystem built from the image.
- Volumes and bind mounts stay writable regardless, since the restriction applies only to the container’s own root filesystem.
- tmpfs mounts provide safe, memory-backed writable exceptions for genuinely temporary data, discarded automatically when the container stops.
- Most setup effort goes into identifying real write paths first, since unnoticed default writes are the most common cause of crashes after enabling this.
- The security payoff is significant: it removes an attacker’s ability to use the container’s own disk for tampering or persistence.
- It is one layer of defense in depth, not a complete security solution on its own.
- Orchestrators and hardening benchmarks widely treat this as a baseline expectation, not an advanced or optional configuration.