Read-only containers restrict writes to the container’s root filesystem while allowing deliberately configured writable locations. They can reduce accidental state changes and make runtime assumptions clearer, but they do not make an application incapable of writing anywhere. Volumes, temporary mounts, host integrations, and network access still need review.
A reliable rollout starts by discovering what the application writes. The goal is to replace an unrestricted filesystem habit with documented storage ownership, not to add a flag and hope the service continues working.
Inventory normal and exceptional writes
List writes during startup, ordinary requests, background tasks, shutdown, and error handling. Applications may create temporary files, caches, PID files, compiled assets, uploads, or local databases. A successful initial health check can miss a write that occurs only during an uncommon operation.
Distinguish durable business data from disposable working material. A temporary cache should not accidentally become the only copy of a customer’s upload. Conversely, a durable volume should not be used as an unlimited scratch directory because it happens to be writable.
Inspect the container image and entrypoint. Package installation, configuration rewriting, and permission changes at startup may conflict with a read-only root. Move appropriate build-time work into the image build and design runtime configuration through supported interfaces.
Understand the root-filesystem boundary
Docker’s read-only setting mounts the container root filesystem read-only. Writable volumes or other explicitly configured locations remain separate. This means the application can still modify data on a writable mount, even when it cannot alter the image-backed filesystem.
Do not confuse root filesystem access with the process’s user identity. A process running as root with a read-only root has a different boundary from a nonroot process with narrow mounts. Review user, capabilities, privilege escalation, and mount permissions independently.
A read-only filesystem also does not prevent memory changes, network requests, or every kernel interaction. Use it as one component of a defense-in-depth design rather than describing it as a complete sandbox.
Add only necessary writable locations
For disposable files, consider a supported temporary filesystem mount with an appropriate size limit and permissions. For durable state, use an approved volume with clear backup, ownership, and access requirements. Each mount should have a purpose and a responsible owner.
The following illustrates a limited test configuration for an image you have already reviewed:
docker run --read-only --tmpfs /tmp:rw,size=64m approved-app:tested
The size and path are examples, not universal recommendations. Verify platform support and the application’s actual requirements. Test what happens when temporary storage is full so the failure is controlled rather than an unexplained crash.
Avoid mounting large host directories merely to make startup errors disappear. That can broaden access beyond the original root-filesystem boundary. Prefer the smallest required path and review whether the mount should itself be read-only.
Keep host authority out of the container
A writable host integration can outweigh the benefits of a read-only root. Docker’s documentation warns that exposing the daemon socket grants powerful access to the host’s Docker daemon. Do not attach it casually to an application container for convenience.
Review privileged mode, host namespaces, devices, and capabilities alongside filesystem settings. A configuration can display read-only while still granting authority that allows consequential host changes through another interface.
Choose a narrow runtime identity and ensure the application can read only the secrets and configuration it requires. A restricted root filesystem does not prevent a process from copying a readable credential to a network destination. Network policy and secret handling remain separate controls.
Adapt logs and runtime configuration
Prefer the container platform’s supported logging path rather than writing an unlimited log file inside an application directory. If local logs are necessary, use a bounded writable location with reviewed rotation and collection. Ensure failure logs do not include private configuration values.
Keep runtime configuration immutable where possible. If a framework normally rewrites a configuration file, use its supported external configuration mechanism or allocate a narrowly scoped generated-file path. Do not silently make the whole application tree writable.
Check libraries that create caches in a user’s home directory or another default location. Configure their paths deliberately and test the actual runtime user. An image that works for root during development can fail after both nonroot and read-only controls are enabled.
Test complete workflows before enforcement
Exercise uploads, exports, scheduled jobs, errors, graceful shutdown, and restart. Run under the final mount layout and identity, not a relaxed developer configuration. Inspect failed writes to identify a legitimate requirement or an unwanted runtime mutation.
Verify persistence semantics. Restart the container and check which data should survive and which should disappear. A writable temporary mount is not a durable storage plan, and a named volume still needs backup and restoration checks for important data.
Test resource exhaustion and permissions failures. The application should return an appropriate error or retry according to its design without exposing internal paths or repeatedly filling another location. A full scratch mount must not trigger an uncontrolled fallback to a sensitive host path.
Keep the configuration maintainable
Document every writable path, expected size, owner, retention, and recovery requirement. This inventory makes application upgrades easier to review because a newly introduced write has a clear decision process. Do not approve broad new mounts solely to avoid investigating a changed dependency.
A practical example is a web service that writes compiled templates under its installation directory. Build stable templates into the image where appropriate, or redirect the genuinely runtime cache to a bounded scratch path. Then verify ordinary requests and cold-start behavior under the final restriction.
Monitor storage pressure and unexpected write failures after rollout. A read-only setting can reveal previously hidden assumptions, but those observations still need owners and a response path. Security controls should make the service’s state model clearer, not merely produce more ignored errors.
Frequently asked questions
Does read-only mean the container cannot write data?
No. Explicit writable mounts and other interfaces remain available. Review every storage and host boundary separately.
Can I fix errors by mounting the whole application directory?
That may erase much of the intended restriction. Identify the required write and provide a narrowly scoped location instead.
Where should I check runtime behavior?
Read the Docker container run reference. For reducing process privilege separately, see our rootless Docker guide.