Skip to content

prefer-read-only-bind-mount

Compose bind mounts are writable by default. When a service only reads a host directory, add :ro or read_only: true so a compromised container cannot change files on the host.

On this page

A Compose bind mount is read-write unless you say otherwise, and most of them never needed to be: certificates, model weights, static assets, and configuration are read by the service and written by you. This rule reports bind mounts whose host source lies outside the Compose project (an absolute path, ~, or ../) and that are not marked read-only, in either the short syntax (:ro) or the long syntax (read_only: true). Relative mounts inside the project (./src:/app/src) are the usual dev-loop mounts and are not reported; neither is the Docker socket, which has its own rule.

What the rule catches

compose.yaml — host paths the service only reads, mounted writable
services:
  web:
    image: nginx:1.27-alpine
    volumes:
      - /opt/certs:/etc/nginx/certs
      - type: bind
        source: /srv/site
        target: /usr/share/nginx/html

Scanning this file reports:

ℹ INFO [docker-doctor/prefer-read-only-bind-mount]
  Service 'web' bind-mounts host path '/opt/certs' read-write. A writable host mount lets the container change files outside the project, and is the foothold for VM-escape bugs like CVE-2026-77179.

Why it matters

Read-only is the cheapest hardening a bind mount can get, and it removes a whole class of attack rather than one bug. A container that can write to a host directory can plant files where the host will later execute or trust them. On Docker Desktop and Docker Sandboxes, where containers run in a VM and the mount is served from the host, a writable mount is also the precondition for VM-escape bugs: CVE-2026-77179 needed to create a file, delete its directory, and replace it with a symlink, none of which works on a read-only mount. The isolation that a VM promises holds only for the paths the guest cannot write.

How to fix it

Append :ro to the short syntax, or set read_only: true in the long syntax, unless the service must write to the host path.

compose.yaml — the same mounts, read-only
services:
  web:
    image: nginx:1.27-alpine
    volumes:
      - /opt/certs:/etc/nginx/certs:ro
      - type: bind
        source: /srv/site
        target: /usr/share/nginx/html
        read_only: true

Append :ro to a short-syntax mount, or add read_only: true under a long-syntax one. If the service does write there, ask whether the host directory is really the right place: a named volume keeps the data without exposing a host path at all, and no-broad-bind-mount covers the case where the path is not just writable but far too wide.

The rule reports at info severity, so it does not affect the health score by much on its own. Raise it to warning in docker-doctor.config.json for services that run untrusted code, such as coding agents and CI runners.

Rule details

  • Rule keydocker-doctor/prefer-read-only-bind-mount
  • Category — Compose
  • Default severityinfo
  • Applies to — Docker Compose files

Explain this rule from the CLI:

npx @docker-doctor/cli@latest rules explain docker-doctor/prefer-read-only-bind-mount

Change its severity — or turn it off — in your config file:

// docker-doctor.config.ts
export default {
  rules: {
    "docker-doctor/prefer-read-only-bind-mount": "off",
  },
};

Severity affects the health score: error findings cost more points than warning, and info costs the least.