A container's filesystem is ephemeral : it's a thin writable layer on top of the image, and it's destroyed with the container. Anything the container writes there (database rows, uploads, logs) is gone when the container is removed. Mounts solve this by connecting a path inside the container to storage on the host machine, so the files actually live on the host's disk and outlive any container that uses them.
There are two kinds, and they differ in who controls the host-side location:
- Bind mount : maps a specific host folder you choose into the container. You know the exact path on disk, and you can edit those files directly. Ideal for development, where you want the container to see your source code as you change it.
- Named volume : storage Docker creates and manages, identified by a name rather
than a path (
docker volume create pgdata). Docker decides where it lives on disk; you just refer to it by name. Ideal for data a container must persist but you never edit by hand, like a database's files.
The same mechanism covers two different needs: a named volume is about persistence (keep data across container restarts and rebuilds), while a bind mount is usually about sharing (the host and container working on the same files at the same time).
Bind mounts for live-reload
The most common development use is bind-mounting your source code. The host folder and the container path point at the same files, so a change on either side is visible to the other instantly, with no copy step in between.
# Mount dev/app of the home folder into /app in the container
docker run -p 3000:3000 -v /Users/bobsmith/dev/app:/app my-api
# To do the same thing regardless of the name of your home folder
docker run -p 3000:3000 -v "$(pwd)"/dev/app:/app my-api
The -v host:container flag maps a host folder to a path inside the container. The
container reads your source directly instead of the copy baked into the image at build time. Add
a file watcher inside the container (nodemon for Node, for example) and a save on
the host triggers a reload — no rebuild, no restart.
Heads-up: a bind mount replaces what the image had at that path. During the build,
npm installcreated/app/node_modulesinside the image, but when you mount your project folder over/app, the container sees your folder instead, which probably has nonode_modules(or one built for the wrong OS). The app crashes with "module not found." The fix is an extra flag,-v /app/node_modules, which tells Docker to keep that one subfolder as container-managed storage. Your source comes from the host; the installed dependencies stay from the image's build.
-v /app/node_modules, shields the built dependencies from the bind mount so live-reload works without a "module not found" crash. Named volumes for persistence
For data you want to keep but never edit by hand — a database's files, for instance — use a named volume instead. You refer to it by name and let Docker manage where it lives on disk.
# Create a named volume once
docker volume create pgdata
# Mount it by name; Docker decides where it lives on disk
docker run -v pgdata:/var/lib/postgresql/data postgres
The same -v flag does the work, but the left side is a volume name rather than a host
path. The volume outlives the container, so removing and recreating the postgres
container keeps the data intact.
Check your understanding
How live-reload works without rebuilding
How does bind-mounting your source folder enable live-reload without rebuilding the image?