Skip to main content
CodeOath
← All posts

Docker105 min total · 19 parts

Docker Fundamentals: Images, Containers, and Writing a Good Dockerfile

Part 12 of 19 · ~3 min

Volumes, Bind Mounts, and tmpfs

Remove a container and its writable layer goes with it, no exceptions — which is a problem the moment something in there needs to outlast that container specifically. Postgres's data, a customer's uploaded receipts, anything that would actually hurt to lose has to be stored somewhere the container's own lifecycle can't touch.

Who owns itWhere it earns its keep
VolumeDocker, in a location it manages on the hostPostgres's data directory — Docker owns the lifecycle, independent of any one container
Bind mountYou — a specific path on your own machineLocal dev only: point it at your working copy and edits show up inside the container as you save them
tmpfs mountNowhere — it exists only in the host's RAMAnything that genuinely shouldn't touch a disk, even for a moment
docker run -v pgdata:/var/lib/postgresql/data postgres:16   # named volume
docker run -v $(pwd)/web:/app/src snapledger-web             # bind mount for live dev editing
docker run --tmpfs /app/scratch snapledger-worker            # in-memory, gone on container stop

pgdata is the fix for a scare you have early on:

docker compose up -d --force-recreate postgres

You watch the old postgres container get torn down and a brand-new one spin up in its place, and for a horrible thirty seconds you're certain every expense row snapledger has ever recorded is gone with it. It isn't. The container was disposable — recreating it destroys the process and its writable layer, nothing more — but pgdata was never attached to the container, it was attached to the volume, and volumes outlive whatever container happens to be mounting them at any given moment. docker compose down alone respects that same boundary; only the explicit -v flag reaches into it.

The bind mount is what finally lets Priya stop rebuilding an image every time she tweaks a line of web's code on her Intel Mac — the exact machine from the opening chapter. Point the mount at her actual working copy on disk, and the container is now looking at her live source tree directly; save a change in her editor and the container sees it on its next request, nothing rebuilt in between, and the receipt-parsing quirk that made her machine behave differently from yours and Marcus's back in the intro is now something she can poke at directly, live, inside the same container image everyone else is using — rather than debugging it against whatever happens to be installed natively on her laptop. That convenience is specifically a development-time trick, though — a production image gets its source the ordinary way, baked in with COPY when the image is built, since there's no reason a production host would ever have snapledger's source checked out on it, let alone at a path a container could safely assume exists.

tmpfs is the one of the three you almost skip, because on paper it looks like a narrower version of a bind mount, and its actual justification isn't performance. When worker unpacks a receipt image to render its pages, that's a customer's real bank or purchase data sitting somewhere on disk, however briefly. Mounting /app/scratch — the same path the VOLUME instruction flagged back in the Dockerfile-instructions chapter — as tmpfs means those bytes live only in RAM for the few seconds they actually exist, and vanish the instant the container stops, restarts, or gets OOM-killed. There's nothing to accidentally leave behind on a host disk after the job finishes, and nothing to remember to clean up, because there was never anything durable there to begin with.