The `docker run` command accepts both the `-v`/`--volume` flag and the `--mount` flag for attaching storage. What are the practical differences between the two, and which would you use?
answer
- -v = positional triple, --mount = key=value
- -v auto-creates missing bind source (empty, root-owned)
- --mount type=bind errors on missing source
- first field shape decides volume vs bind for -v
- --mount required for swarm services; exposes tmpfs-size, volume-opt
basics
~20 s-v is a short colon-separated triple; --mount is explicit key=value pairs with type=. The dangerous difference: -v silently creates a missing host directory (empty, root-owned), while --mount type=bind fails fast. --mount is also more readable and exposes more options.
solid answer
~50 sThey do the same job with different ergonomics. `-v host-or-name:/target:opts` packs everything into one positional string. Docker decides what you meant by the first field: if it looks like a path (starts with `/` or `./`) it is a bind mount, otherwise it is a named volume. If the bind source does not exist, `-v` **creates it** as an empty root-owned directory — so a typo in the host path silently gives your container an empty directory instead of your config, and the app starts with defaults or crashes confusingly. `--mount type=bind,source=...,target=...,readonly` is verbose but explicit: the type is stated, not inferred, options are named rather than positional, and a missing bind source is an error instead of a surprise directory. It also exposes options `-v` cannot express as cleanly, such as `volume-driver`/`volume-opt`, `tmpfs-size`, and `bind-propagation`. In practice: `-v` for a quick interactive run, `--mount` in anything checked into a repo, scripted, or run in production.
code
bash · 12 lines# short form
docker run -v /srv/conf:/etc/app:ro nginx
docker run -v webdata:/usr/share/nginx/html nginx
# explicit form
docker run --mount type=bind,source=/srv/conf,target=/etc/app,readonly nginx
docker run --mount type=volume,source=webdata,target=/usr/share/nginx/html nginx
# typo in the host path:
# -v -> creates an empty root-owned /srv/cnf and starts anyway
# --mount -> "invalid mount config: bind source path does not exist: /srv/cnf"
docker run --mount type=bind,source=/srv/cnf,target=/etc/app,readonly nginxgo deeper
Know both spellings and that they do the same thing; be able to write a read-only bind mount in each form.
Lead with the fail-fast difference on a missing bind source and the shape-based volume-vs-bind inference in -v; these are the parts that bite people.
Frame it as a convention: --mount or Compose long syntax in anything committed or automated, -v only for ad-hoc shells, because silent empty directories are a real incident cause.
Talk about making the safe form the default across the org — lint or template the run/Compose files — and note that the explicit form is also what maps cleanly onto orchestrator mount specs later.
## Two spellings of one feature Docker grew `-v` first, back when it only had to express "host path colon container path". As volumes gained drivers, options and types, the flag was stretched with extra colon-separated fields until it became ambiguous. `--mount` was added later as the explicit, self-documenting form. Both reach the same code path; nothing you can do with one is fundamentally impossible with the other, but the failure modes differ. ## The `-v` form The syntax is `-v <source>:<target>[:<options>]`. - With **one** field (`-v /data`) you get an anonymous volume at that container path. - With **two** fields, the first is either a named volume or a host path, decided purely by shape: a leading `/` or `./` means bind mount, anything else means named volume. So `-v mydata:/data` mounts a volume named `mydata`, while `-v ./mydata:/data` binds a directory. - The third field is a comma-separated option list: `ro` / `rw`, SELinux relabel flags `z` and `Z`, propagation modes such as `rslave`, and on some platforms consistency hints. Two behaviours make `-v` risky in automation: 1. **Auto-creation of a missing bind source.** `-v /srv/apps/conf:/etc/app` when `/srv/apps/cnf` was the real path (or the path exists only on your laptop) does not fail. Docker creates `/srv/apps/conf` as an empty directory owned by root, mounts it, and the container comes up with an empty config directory. Debugging that costs more time than the flag ever saved. 2. **Shape-based inference.** A relative-looking source that you meant as a path becomes a *named volume* instead, and again nothing errors — you just get an empty volume that then gets pre-populated from the image, which can look like the mount "worked". ## The `--mount` form The syntax is a comma-separated list of `key=value` pairs, order-independent: - `type=bind|volume|tmpfs` (defaults to `volume` if omitted) - `source=` / `src=` — host path for binds, volume name for volumes; omitted for tmpfs and for anonymous volumes - `destination=` / `dst=` / `target=` — the path inside the container - `readonly` (or `readonly=true`, `ro=true`) instead of a positional `ro` - type-specific extras: `volume-driver=`, `volume-opt=`, `volume-nocopy` (skip pre-populating an empty volume from the image), `tmpfs-size=`, `tmpfs-mode=`, `bind-propagation=`, and on recent Docker versions `bind-recursive=` to control whether read-only applies to nested submounts. With `type=bind`, a missing `source` is a hard error. That single property is the strongest practical argument for `--mount` in anything unattended: a broken path fails at start time with a clear message rather than becoming a silent empty directory. `--mount` is also the only form accepted by `docker service create` in Swarm, which is why examples in orchestration contexts use it. ## Compose Compose supports both a short string form (`- ./conf:/etc/app:ro`) that mirrors `-v`, and a long form that mirrors `--mount`: The long form spells out `type`, `source`, `target`, `read_only`, and per-type blocks such as `bind:` and `tmpfs:`. It is more lines, but it is diffable, reviewable, and cannot be misread by a human skimming a wall of colons. Note one Compose-specific detail: with the *short* form, a relative bind path that does not exist is created, and with `type: bind` in the long form Compose can be told to create the host path explicitly via `create_host_path`. ## Which to use A defensible answer in an interview: - Interactive, throwaway commands: `-v`, because it is short and you will notice a mistake immediately. - Anything committed — Compose files, CI scripts, systemd units, deploy tooling: `--mount` (or Compose long syntax), because explicit types, named options and fail-fast behaviour matter more than brevity when nobody is watching the terminal. The deeper point the question is testing is whether you know that `-v` *infers* things and *creates* things. Candidates who have been burned by a container that came up healthy but empty always know this; it is a good proxy for real operational time with Docker.
- What exactly does Docker do with `-v /path/that/does/not/exist:/data`?It creates the host path as an empty directory owned by root, then bind-mounts it. The container starts successfully with an empty `/data`, which is why a mistyped path usually shows up later as missing config or lost data rather than as a startup error. `--mount type=bind` refuses to start instead.
- How does Docker decide whether `-v foo:/data` means a named volume or a bind mount?By the shape of the first field. A source starting with `/` or `./` is treated as a host path and produces a bind mount; anything else is treated as a volume name. So `foo:/data` is a named volume called `foo`, and if that volume does not exist Docker creates it — silently, which is why implicit inference is a poor fit for scripts.
saying these in an interview costs you the question
- Claiming `--mount` and `-v` behave identically for a missing host path.
- Thinking `-v` cannot express read-only mounts (it can, as the third field: `:ro`).
- Saying `--mount` is deprecated, or that `-v` is deprecated — both are supported; only the ergonomics differ.
- Assuming the first field of `-v` is always a host path, so a relative-looking name accidentally creates a named volume.