Dev.to · 9 min read

Docker Volumes vs Bind Mounts: Which to Use

Docker Volumes vs Bind Mounts: Which to Use

Originally published on DevToolHub. Use a Docker volume when the container owns the data, such as a database or a model cache. Use a bind mount when you need to edit the files on your host, such as source code or a config file. The docker volumes vs bind mounts choice also changes startup behavior and speed. It decides how easy it is to lose data, too. So it's worth testing before you pick. I ran everything below on Docker Engine 29.4.3 with Docker Desktop for Mac. I didn't test a Linux host, so I flag the spots where it may differ. What Is the Difference Between Docker Volumes vs Bind Mounts? A volume is storage that Docker creates and manages for you. A bind mount maps a folder that already exists on your host into the container. Docker's volumes documentation calls volumes the preferred way to persist container data. Its bind mounts page points bind mounts at sharing source code and host config files. Volume Bind mount Who manages it Docker You Where data lives Docker's storage area A path you choose on the host Host path must exist No Yes, unless Docker creates it Fills from the image on first use Yes (copy-up) No, it hides the image's files Easy to edit from the host No Yes Best for Databases, caches, app state Source code, config files When Should You Use a Docker Volume? Use a volume for any data the container creates and you want to keep, but never edit by hand. Volumes survive the container, and Docker handles their location. I wrote a file into a volume, let the container exit, and read it back from a new one: docker run --rm -v dvbtest-data:/data alpine sh -c 'echo saved > /data/note.txt' docker run --rm -v dvbtest-data:/data alpine cat /data/note.txt saved The --rm flag deleted both containers, but the volume stayed, and docker volume ls still listed it. docker volume inspect shows where Docker stores it: local /var/lib/docker/volumes/dvbtest-data/_data ⚠️ Note: that path does not exist on a Mac. On Docker Desktop the volume lives inside the Linux VM, so ls on my host returned No such file or directory. If you need to browse the files from your host, a volume is the wrong tool. The same pattern powers the model cache in the Ollama Docker setup guide. Without a named volume, those downloads vanish with the container. When Should You Use a Bind Mount? Use a bind mount when your host and the container need the same files. Source code you edit in an IDE is the classic case. So is a config file you maintain. Per the docs, a bind mount gives the container write access to your host files by default. Add :ro when the container only needs to read: docker run --rm -v /tmp/dvb/auto-created:/data:ro alpine sh -c 'echo z > /data/y' sh: can't create /data/y: Read-only file system The container can't change the host folder now. That matters because a bug or a bad command inside the container can otherwise delete files on your machine. On ownership: my container wrote as root, and the file still showed up on my Mac owned by my own user. That mapping is a Docker Desktop behavior. I didn't test Linux, where a root process in the container may leave root-owned files on the host. Does a Mount Hide the Files Already in the Image? A bind mount always hides what the image has in that folder. A new named volume does the opposite: Docker copies the image's files into it first. I mounted both over the NGINX web root, /usr/share/nginx/html, which ships with index.html and 50x.html. An empty host folder as a bind mount: total 4 drwxr-xr-x 2 root root 64 Nov 5 13:57 . That folder looked empty, and index.html was gone from view. A new named volume at the same path: 50x.html index.html Docker copied both files into the volume on first use. To switch that copy off, use volume-nocopy: docker run --rm --mount type=volume,src=dvbtest-nocopy,dst=/usr/share/nginx/html,volume-nocopy nginx:alpine ls -A /usr/share/nginx/html That listed zero files. Note the copy-up only happens when the volume is empty. A volume that already holds data hides the image's files, just like a bind mount. Docker Volumes vs Bind Mounts With -v and --mount The two flags differ in one way that bites people: what happens when the host path is missing. With -v, Docker silently creates the folder. With --mount, it stops with an error. Here is -v pointing at a path that didn't exist: docker run --rm -v /tmp/dvb/auto-created:/data alpine sh -c 'echo hi > /data/x' It ran fine and created /tmp/dvb/auto-created on my host. The same mismatch with --mount: docker run --rm --mount type=bind,src=/tmp/dvb/nope,dst=/data alpine true docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /host_mnt/private/tmp/dvb/nope It exited with code 125. The /host_mnt/... prefix looks like Docker Desktop's file-sharing path translation. Add bind-create-src to the --mount options if you want the folder created, as the docs describe. A typo in a -v path therefore creates an empty folder and starts the container happily. That is a classic source of "my config isn't loading" bugs. Prefer --mount in scripts. There is a second trap in the -v syntax. A bare name is a volume, and a path starting with ./ is a bind mount: docker run --rm -v data:/app alpine sh -c 'echo a > /app/f' # creates a named volume "data" docker run --rm -v ./data:/app alpine sh -c 'echo b > /app/f' # writes to ./data on your host After the first command, docker volume ls listed a volume called data and my project folder stayed empty. The second wrote f into ./data. Forget the ./ and you create a hidden volume instead of the folder you meant. How Much Slower Is a Bind Mount on Docker Desktop? On my Mac, writing 3,000 small files into a bind mount took about five times longer than into a volume. Each run launched a container and created the files with a shell loop, so both numbers include roughly the same startup cost: Run Bind mount Named volume 1 4.49 s 0.70 s 2 3.27 s 0.73 s 3 3.78 s 0.67 s This is a rough test: three runs, one machine, one workload. The gap likely comes from Docker Desktop crossing the VM boundary on every file operation. Expect it to matter for node_modules, package caches and builds. On a Linux host, a bind mount is a direct path and I'd expect no such penalty, but I haven't measured it. How Do You Delete Volumes Safely? Containers don't delete volumes, so unused ones pile up until you remove them. Compose shows the difference well. I started a service with a volume, then ran docker compose down. The volume survived: dvbtest_appdata ⚠️ Note: docker compose down -v deletes every named volume declared in that Compose file, along with its data. Run docker compose down without -v unless you really want a clean slate. docker compose down -v Volume dvbtest_appdata Removed The earlier run's log line was still there when I brought the service back up, so the data had persisted until I used -v. For one volume, use docker volume rm . docker volume prune removes all unused volumes, so read what it lists first. Docker Volumes vs Bind Mounts: Which Should You Use? Pick a volume for data the container owns and a bind mount for files you edit. If you're unsure, start with a volume, because it's portable and faster on Docker Desktop. Situation Use Databases, caches, uploads, anything you back up with Docker Volume Source code, config files, build output you want on your host Bind mount Config the container should read but never change Bind mount with :ro Temporary state you don't need to keep (Docker's docs point to tmpfs) Neither For the rest of your container setup, the Docker best practices guide covers the surrounding habits, and the Dockerfile best practices cover the image side. Frequently Asked Questions Q: Should I use a Docker volume or a bind mount for a database? A: Use a volume. Docker manages its location, it survives container removal, and on Docker Desktop it avoided the slowdown I saw with bind mounts. A bind mount makes sense for a database only if you must inspect the files directly from the host. Q: What is the difference between -v and --mount? A: --mount uses explicit key=value options and errors if a bind source path is missing. -v is shorter and silently creates a missing host folder. Both can create volumes or bind mounts, but --mount is the clearer choice for scripts. Q: Do Docker volumes persist after the container is deleted? A: Yes. Named volumes stay until you remove them with docker volume rm, docker volume prune or docker compose down -v. Anonymous volumes created with docker run --rm are removed along with the container. Q: Why is my bind mount empty inside the container? A: A bind mount hides whatever the image has at that path. If the host folder is empty, the container sees an empty folder. A new named volume works differently, because Docker copies the image's files into it first. Q: Are Docker bind mounts slower than volumes? A: On Docker Desktop for Mac, yes. Creating 3,000 small files took 3.3 to 4.5 seconds on a bind mount and about 0.7 seconds on a volume in my tests. I didn't measure a Linux host. Quick Summary: Docker manages a volume, while a bind mount maps a host folder you choose Volumes survived container removal in my test, and docker compose down -v is what deletes them New volumes get the image's files copied in first, but a bind mount hides them -v creates a missing host path silently, and --mount stops with exit code 125 -v data:/app makes a volume, and -v ./data:/app makes a bind mount Creating 3,000 files took about five times longer on a bind mount than a volume on Docker Desktop for Mac Any bind mount the container should only read needs :ro Before your next docker run -v, decide which side owns the data. In the docker volumes vs bind mounts choice, that answer settles it. Container-owned data goes in a volume. Files you edit go in a bind mount.

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More AI & Machine Learning News