Read-Only Filesystems

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

Before getting into mechanics, it helps to be clear about exactly what becomes read-only, and what does not.

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.

Everyday Analogy

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

A handful of distinct pieces determine what a “read-only” container can and cannot do.
Container Root Filesystem

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.

read-only Flag

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.

tmpfs Mount

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.

Volumes & Bind Mounts

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.

Application Write Paths

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.

Orchestrator Policy

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

Here is what actually happens at the filesystem level when a container is started this way.

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.

i
Key Distinction

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

Tracing a few different write attempts through a locked-down container shows exactly where the boundary sits.
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

This is the chapter this entire topic really exists for — read-only filesystems are one of the more effective, low-cost defense-in-depth measures available for containers.

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.

!
Defense in Depth, Not a Complete Solution

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-01 Recommended
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-02 Recommended
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-01 Avoid
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-02 Avoid
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

Do

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.

Do

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.

Do

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.

Avoid

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.

Avoid

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.

Avoid

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

1

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.

2

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.

3

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.

4

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

Q1Does a read-only filesystem mean a container can never write anything at all?
No. It means the container’s root filesystem specifically cannot be written to. Volumes, bind mounts, and tmpfs mounts attached to the container remain writable unless separately restricted.
Q2What happens to data written to a tmpfs mount when the container stops?
It is discarded. A tmpfs mount is backed by memory rather than persistent storage, so anything written there disappears once the container stops, which makes it suitable only for genuinely temporary, disposable data.
Q3Why does an application sometimes crash immediately after enabling a read-only root filesystem?
Most commonly because it was writing to a path on the root filesystem that nobody had noticed — a default cache directory, a lock file, a log file — and that write now fails outright. Identifying and redirecting those paths to a tmpfs mount or volume usually resolves it.
Q4Is a read-only filesystem enough security on its own?
No. It is a strong, effective layer of defense in depth, but it does not prevent reading existing files, network-based exploitation, or attacks that do not depend on writing to the container’s disk. It works best combined with other hardening measures.
Q5Can a read-only root filesystem be enforced across an entire deployment, not just one container?
Yes. Container orchestrators commonly provide a policy setting that requires every container in a given deployment or cluster to run with a read-only root filesystem, turning it into an organization-wide standard rather than a per-container decision.
Q6Does a read-only filesystem affect the container’s actual application code at runtime?
It prevents that code from being modified while the container is running, but it does not change how the application behaves otherwise — the application still runs exactly as the image defines it, it simply cannot rewrite its own files afterward.

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.