17.3.2.3 Dev Bind Mount Rule
A focused guide to Dev Bind Mount Rule, connecting core concepts with practical Docker and container operations.
The dev bind mount rule is a simple, deliberate boundary: bind-mounting source code for live editing is a development-only convenience, never a production pattern, since production containers should run exactly the code baked into their image at build time, with no live, host-editable path into their running filesystem at all.
Why the rule exists
A bind-mounted source directory means the container's actual running code can change at any moment, edited directly on the host, entirely outside of the image build process, which is precisely the live-editing convenience that makes local development fast, but is also precisely the property that makes a production deployment's content non-reproducible and untraceable back to a specific, built, version-controlled artifact:
services:
api:
build: .
volumes:
- .:/app
services:
api:
image: registry.example.com/my-api:1.4.2
The first definition is appropriate for development, where editing a file on the host and seeing the change reflected immediately inside the running container is the entire point; the second is appropriate for production, where the running container's code should be fixed, immutable, and traceable to exactly the image it was built from, with nothing able to alter it after the fact.
Drawing the line clearly in Compose override files
Using the layered base-and-override pattern, the bind mount belongs exclusively in a development-specific override, never in the base file that production configuration also builds upon, which structurally prevents it from ever accidentally carrying over into a production deployment:
services:
api:
image: my-api
services:
api:
build: .
volumes:
- .:/app
services:
api:
image: registry.example.com/my-api:1.4.2
Structuring the files this way means production's own override, or the absence of any override applying the bind mount, ensures the bind mount can never appear in a production deployment by mistake, since it simply does not exist anywhere in the production-targeted configuration files at all.
Excluding dependency directories from the bind mount
When bind-mounting an entire project directory for live editing, an accompanying anonymous volume specifically excluding the dependency directory prevents the host's own dependency installation, often built for a different architecture or operating system than the container, from shadowing the one correctly installed inside the container itself:
services:
api:
volumes:
- .:/app
- /app/node_modules
This pattern, covered more specifically elsewhere, is itself part of the broader dev bind mount rule: the bind mount applies to source code specifically, not indiscriminately to every directory, with dependency directories deliberately carved out and excluded from it.
File watching considerations specific to bind mounts
Live code reloading depends on the application's own file-watching mechanism correctly detecting changes made through the bind mount, which can behave differently, sometimes considerably less reliably, depending on the host operating system and the specific file-sharing mechanism in use, particularly on Docker Desktop for macOS and Windows:
nodemon.exec('node server.js', { legacyWatch: true });
Some file-watching libraries support a polling-based fallback mode specifically for situations where the native, event-driven file watching mechanism does not reliably propagate changes through a bind mount's underlying file-sharing layer, which is worth enabling explicitly if live reload appears unreliable on a specific development setup.
Never relying on a bind mount to fix a production issue
It can be tempting, during an urgent production incident, to bind-mount a quick code fix directly into a running production container rather than going through the normal build-and-deploy process, but this directly violates the dev bind mount rule and produces exactly the kind of undocumented, non-reproducible drift the rule exists to prevent:
docker run -v ./hotfix.js:/app/server.js my-api
Even under genuine incident pressure, the correct response is a fast, expedited build-and-deploy cycle, not a bind-mounted, off-the-books patch that leaves no trace in version control or the image's own build history.
Verifying production deployments contain no bind mounts
Periodically confirming that no production deployment configuration includes a bind mount of application source code, through a direct review of the actual configuration in use, catches a violation of this rule before it becomes an established, unnoticed pattern:
docker inspect my-api --format '{{json .Mounts}}' | jq '.[] | select(.Type=="bind")'
Reviewing this output against the expectation that production should show no bind mounts of source code at all, only named volumes for genuine persistent data, confirms the rule is actually being respected in practice rather than assumed.
Common mistakes
- Allowing a development-oriented bind mount to remain present in a configuration file that production deployments also build upon, rather than confining it strictly to a development-only override.
- Bind-mounting an entire project directory without excluding the dependency directory, allowing a host-installed dependency tree to shadow the one correctly installed inside the container.
- Relying on a bind-mounted, off-the-books code patch to resolve a production incident rather than going through the normal, traceable build-and-deploy process even under time pressure.
- Not accounting for file-watching reliability differences across host operating systems and file-sharing mechanisms when live reload through a bind mount does not behave as expected.
- Never directly verifying that production deployments are genuinely free of source-code bind mounts, assuming the rule is followed without confirming it.
The dev bind mount rule is simple to state and valuable specifically because of its simplicity: bind-mounting source code is a development convenience that should never appear anywhere in a production deployment's actual configuration, and structuring configuration files so this boundary is enforced structurally, rather than relying on discipline alone, is what keeps the rule genuinely intact over a project's lifetime.