Docker Error Guide: 'mkdir: read-only file system' — Fix Read-Only Containers and Mounts
Fix Docker 'mkdir read-only file system': diagnose --read-only containers, read-only bind mounts, and writes to immutable image layers, then add writable volumes or tmpfs correctly.
- #docker
- #troubleshooting
- #errors
- #volumes
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
An application inside a container tries to create a directory (or write a file) and the kernel rejects it because the target path is mounted read-only:
mkdir /app/cache: read-only file system
The EROFS error means the filesystem backing that path does not permit writes. In Docker this usually comes from a hardened --read-only container, a bind mount added with the :ro flag, or an attempt to write into a location that overlaps a read-only mount.
Symptoms
- The app logs
read-only file systemwhen it tries to create a temp dir, log file, or cache. - The container starts but crashes the moment it writes to disk.
- The same image works without
--read-onlybut fails once security hardening is enabled. - A process can read a mounted config but cannot write next to it.
Common Root Causes
--read-onlyroot filesystem — the container was started with a read-only root for security, and the app writes somewhere without a writable volume.- Read-only bind mount — a volume was added with
:ro(orread_only: truein Compose) but the app needs to write to it. - Writing under a read-only mount point — the app writes to a subpath of a directory mounted read-only.
- Read-only secrets/config mounts — Kubernetes
configMap/secretmounts and Docker configs are read-only by design. - Immutable image layers — attempting to modify files baked into a lower overlay layer in an unexpected way.
Diagnostic Workflow
Check whether the container’s root filesystem was made read-only:
docker inspect --format 'ReadonlyRootfs={{.HostConfig.ReadonlyRootfs}}' myapp
List every mount and its RW/RO flag:
docker inspect --format '{{range .Mounts}}{{.Type}} {{.Source}} -> {{.Destination}} rw={{.RW}}{{println}}{{end}}' myapp
From inside the container, see exactly which mount owns the failing path and whether it is ro:
docker exec -it myapp sh -c 'mount | grep " /app "; touch /app/cache/.probe 2>&1'
A ro in the mount options, or Read-only file system from the touch, confirms it. Check the Compose/run configuration for the flags that caused it:
docker inspect --format '{{json .HostConfig.Tmpfs}}' myapp
Example Root Cause Analysis
After a security review, a service was hardened with --read-only to prevent tampering. It immediately crash-looped with mkdir /app/cache: read-only file system.
Inspecting confirmed the read-only root:
docker inspect --format '{{.HostConfig.ReadonlyRootfs}}' myapp
# true
The application wrote scratch data to /app/cache at startup. With the root filesystem read-only, that write failed. The correct fix was to keep the hardened read-only root but provide writable space exactly where the app needs it — a tmpfs for ephemeral cache and a named volume for anything that must persist:
docker run --read-only \
--tmpfs /app/cache:rw,size=64m \
-v app-data:/app/data \
myapp:1.4.2
The container kept its immutable root filesystem for security while /app/cache and /app/data became writable, and the crash loop stopped.
Prevention Best Practices
- When using
--read-only, explicitly mount writabletmpfsfor scratch/cache paths and named volumes for persistent data. - Design images to write only to well-defined, documented directories so writable mounts are easy to provision.
- Do not add
:roto a mount the application needs to write; reserve read-only for config and secrets. - In Compose, pair
read_only: truewith atmpfs:list and volume mounts for the writable paths. - Validate mount and volume declarations with the Dockerfile validator and follow storage patterns in the Docker stack guide.
Quick Command Reference
docker inspect --format '{{.HostConfig.ReadonlyRootfs}}' myapp # read-only root?
docker inspect --format '{{range .Mounts}}{{.Destination}} rw={{.RW}}{{println}}{{end}}' myapp
docker exec -it myapp sh -c 'mount | grep " /app "' # which mount, ro or rw?
docker run --read-only --tmpfs /app/cache:rw,size=64m -v app-data:/app/data myapp:1.4.2
Conclusion
mkdir: read-only file system is EROFS — the app is writing to a path that is mounted read-only. The cause is almost always a --read-only container or a :ro mount, not a bug in the app. Inspect the container’s ReadonlyRootfs flag and its mounts, find which mount owns the failing path, and provide a writable tmpfs or volume exactly where writes are needed. That keeps your security hardening intact while letting the application do its job.
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.