> ## Documentation Index
> Fetch the complete documentation index at: https://felimet-hub.jmcores.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Where Data Lives: volume, bind mount, tmpfs, and no mount

> Where container data lives and whether it survives deletion: writable layer, volume, bind mount, tmpfs, and WSL 2 mount performance.

export const StorageMounts = ({lang = "zh"}) => {
  const t = lang === "en" ? {
    lead: "Pick a mount mode to see where the data lives.",
    loc: "Where data lives",
    survive: "Survives container rm",
    share: "Shareable across containers",
    perf: "Performance",
    use: "Best for",
    yes: "Yes",
    no: "No",
    cwrite: "Container"
  } : {
    lead: "撥一種掛載模式，看資料實際存在哪。",
    loc: "資料存哪",
    survive: "容器刪除後還在",
    share: "可跨容器共享",
    perf: "效能",
    use: "適用場景",
    yes: "在",
    no: "不在",
    cwrite: "容器"
  };
  const MODES = lang === "en" ? [{
    key: "none",
    name: "No mount",
    sub: "writable layer",
    loc: "Container writable layer (copy-on-write, on top of the image layers)",
    locTag: "Writable layer",
    survive: false,
    share: false,
    perf: "Slower for writes: copy-on-write copies the whole file even for a 1-byte change",
    use: "Throwaway data you never need to keep",
    note: "Anything written without a mount lives only inside the container. docker rm wipes it."
  }, {
    key: "volume",
    name: "Volume",
    sub: "named",
    loc: "/var/lib/docker/volumes/<name>/_data  (under the WSL 2 backend it sits inside the docker-desktop-data VM, not on the Windows filesystem)",
    locTag: "Docker-managed",
    survive: true,
    share: true,
    perf: "Same as native host filesystem; no cross-OS boundary",
    use: "Databases, persistent app data, backup and migration",
    note: "Docker manages it. Survives container deletion until you docker volume rm it. The recommended default for persistence."
  }, {
    key: "bind",
    name: "Bind mount",
    sub: "host path",
    loc: "Any host path you point at (absolute path)",
    locTag: "Host path",
    survive: true,
    share: true,
    perf: "Native on Linux; mounting from /mnt/c (Windows) under WSL 2 is noticeably slower and inotify is unreliable",
    use: "Live-syncing source code in dev, injecting config files",
    note: "The host file stays after the container is gone. With -v a missing relative path becomes a named volume, not a bind mount: use --mount type=bind to avoid the trap."
  }, {
    key: "tmpfs",
    name: "tmpfs",
    sub: "in memory",
    loc: "Host RAM only, never written to disk",
    locTag: "Host memory",
    survive: false,
    share: false,
    perf: "Fastest (RAM I/O)",
    use: "Secrets you do not want on disk, ephemeral scratch cache; Linux only",
    note: "Gone the moment the container stops. Cannot be shared between containers."
  }] : [{
    key: "none",
    name: "無掛載",
    sub: "可寫層",
    loc: "容器可寫層（writable layer，疊在映像層之上，走 copy-on-write）",
    locTag: "可寫層",
    survive: false,
    share: false,
    perf: "寫入較慢：改一個 byte 也要整檔複製（copy-on-write）",
    use: "用完即丟、完全不需要保留的資料",
    note: "沒掛載時寫的東西只活在容器裡，docker rm 就一起消失。"
  }, {
    key: "volume",
    name: "Volume",
    sub: "具名",
    loc: "/var/lib/docker/volumes/<name>/_data（WSL 2 後端下這條路徑在 docker-desktop-data VM 裡，不在 Windows 檔案系統）",
    locTag: "Docker 管理",
    survive: true,
    share: true,
    perf: "等同主機檔案系統原生速度，不跨 OS 邊界",
    use: "資料庫、需持久化的應用資料、備份與遷移",
    note: "由 Docker 管理。容器刪掉 volume 仍在，要明確 docker volume rm 才會消失。持久化的預設首選。"
  }, {
    key: "bind",
    name: "Bind mount",
    sub: "主機路徑",
    loc: "你指定的任一主機路徑（絕對路徑）",
    locTag: "主機路徑",
    survive: true,
    share: true,
    perf: "Linux 原生速度；WSL 2 下從 /mnt/c（Windows）掛載會明顯變慢，且 inotify 不可靠",
    use: "開發時即時同步程式碼、注入設定檔",
    note: "容器刪了主機檔案還在。用 -v 時，找不到的相對路徑會被當成具名 volume 而非 bind mount，改用 --mount type=bind 杜絕這個雷。"
  }, {
    key: "tmpfs",
    name: "tmpfs",
    sub: "記憶體",
    loc: "只在主機記憶體（RAM），不落磁碟",
    locTag: "主機記憶體",
    survive: false,
    share: false,
    perf: "最快（RAM I/O）",
    use: "不想落地的敏感憑證、暫存快取；僅限 Linux",
    note: "容器一停就消失，不能跨容器共享。"
  }];
  const [sel, setSel] = useState(1);
  const idx = Math.min(sel, MODES.length - 1);
  const cur = MODES[idx];
  const css = `
  .sm-root{--sm-bg:#FAF8F3;--sm-surface:rgba(0,0,0,0.025);--sm-border:rgba(0,0,0,0.09);--sm-text:#2b2722;--sm-dim:#6f6a62;--sm-faint:#8a8378;--sm-accent:#bf7551;--sm-ok:#5f8a52;--sm-warn:#c0792f;border:1px solid var(--sm-border);border-radius:14px;background:var(--sm-bg);color:var(--sm-text);overflow:hidden;font-family:ui-sans-serif,system-ui,"Noto Sans TC",sans-serif;}
  .dark .sm-root{--sm-bg:#1b1a18;--sm-surface:rgba(255,255,255,0.03);--sm-border:rgba(255,255,255,0.08);--sm-text:#e7e3da;--sm-dim:#a8a299;--sm-faint:#8a8378;--sm-accent:#cf8a68;--sm-ok:#86b274;--sm-warn:#d7a043;}
  .sm-head{padding:13px 18px 11px;border-bottom:1px solid var(--sm-border);font-size:12.5px;color:var(--sm-dim);display:flex;align-items:center;gap:8px;}
  .sm-head-ic{color:var(--sm-accent);flex-shrink:0;}
  .sm-tabs{display:flex;gap:8px;padding:14px 16px 4px;flex-wrap:wrap;}
  .sm-tab{flex:1 1 110px;min-width:104px;text-align:left;background:transparent;border:1px solid var(--sm-border);border-radius:11px;padding:10px 12px;cursor:pointer;color:inherit;font:inherit;transition:border-color .15s,background .15s,box-shadow .15s;}
  .sm-tab:hover{background:var(--sm-surface);}
  .sm-tab-on{border-color:rgba(191,117,81,.5);background:rgba(191,117,81,.07);box-shadow:0 0 0 3px rgba(191,117,81,.1);}
  .sm-tab-name{font-size:14px;font-weight:650;line-height:1.25;}
  .sm-tab-on .sm-tab-name{color:var(--sm-accent);}
  .sm-tab-sub{font-size:11.5px;color:var(--sm-faint);margin-top:2px;}
  .sm-diagram{display:flex;align-items:center;gap:10px;padding:14px 16px 4px;}
  .sm-node{flex:1 1 0;min-width:0;border:1px solid var(--sm-border);border-radius:10px;padding:10px 12px;background:var(--sm-surface);}
  .sm-node-l{font-size:11px;text-transform:uppercase;letter-spacing:.4px;color:var(--sm-faint);margin-bottom:3px;}
  .sm-node-v{font-size:13.5px;font-weight:600;}
  .sm-arrow{flex-shrink:0;display:flex;flex-direction:column;align-items:center;color:var(--sm-dim);font-size:10.5px;gap:1px;}
  .sm-arrow svg{width:26px;height:18px;}
  .sm-loc-ok{border-color:rgba(95,138,82,.4);background:rgba(95,138,82,.08);}
  .sm-loc-eph{border-color:rgba(192,121,47,.4);background:rgba(192,121,47,.08);}
  .sm-loc-ok .sm-node-v{color:var(--sm-ok);}
  .sm-loc-eph .sm-node-v{color:var(--sm-warn);}
  .sm-body{padding:8px 16px 16px;}
  .sm-specs{border:1px solid var(--sm-border);border-radius:10px;overflow:hidden;margin-bottom:11px;}
  .sm-row{display:flex;border-top:1px solid var(--sm-border);font-size:13px;}
  .sm-row:first-child{border-top:none;}
  .sm-k{width:34%;flex-shrink:0;padding:8px 12px;color:var(--sm-dim);background:var(--sm-surface);}
  .sm-v{flex:1 1 0;min-width:0;padding:8px 12px;font-weight:500;}
  .sm-pill{display:inline-flex;align-items:center;gap:5px;font-size:12.5px;font-weight:650;}
  .sm-pill-ok{color:var(--sm-ok);}
  .sm-pill-no{color:var(--sm-warn);}
  .sm-dot{width:8px;height:8px;border-radius:50%;display:inline-block;}
  .sm-dot-ok{background:var(--sm-ok);}
  .sm-dot-no{background:var(--sm-warn);}
  .sm-note{font-size:13px;line-height:1.6;color:var(--sm-dim);padding:0 2px;}
  @media (max-width:600px){.sm-diagram{flex-direction:column;align-items:stretch;}.sm-arrow{flex-direction:row;}.sm-arrow svg{transform:rotate(90deg);}.sm-k{width:42%;}}
  `;
  const Pill = ({ok}) => <span className={"sm-pill " + (ok ? "sm-pill-ok" : "sm-pill-no")}>
      <span className={"sm-dot " + (ok ? "sm-dot-ok" : "sm-dot-no")} />
      {ok ? t.yes : t.no}
    </span>;
  const locClass = cur.survive ? "sm-loc-ok" : "sm-loc-eph";
  return <div className="sm-root">
      <style>{css}</style>
      <div className="sm-head">
        <svg className="sm-head-ic" xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3" /><path d="M3 5v14a9 3 0 0 0 18 0V5" /><path d="M3 12a9 3 0 0 0 18 0" /></svg>
        {t.lead}
      </div>
      <div className="sm-tabs">
        {MODES.map((m, i) => <button key={m.key} type="button" className={"sm-tab" + (i === idx ? " sm-tab-on" : "")} onClick={() => setSel(i)}>
            <div className="sm-tab-name">{m.name}</div>
            <div className="sm-tab-sub">{m.sub}</div>
          </button>)}
      </div>
      <div className="sm-diagram">
        <div className="sm-node">
          <div className="sm-node-l">{t.cwrite}</div>
          <div className="sm-node-v">docker run …</div>
        </div>
        <div className="sm-arrow">
          <svg viewBox="0 0 26 18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="2" y1="9" x2="22" y2="9" /><polyline points="16,3 22,9 16,15" /></svg>
          <span>write</span>
        </div>
        <div className={"sm-node " + locClass}>
          <div className="sm-node-l">{t.loc}</div>
          <div className="sm-node-v">{cur.locTag}</div>
        </div>
      </div>
      <div className="sm-body">
        <div className="sm-specs">
          <div className="sm-row"><div className="sm-k">{t.loc}</div><div className="sm-v">{cur.loc}</div></div>
          <div className="sm-row"><div className="sm-k">{t.survive}</div><div className="sm-v"><Pill ok={cur.survive} /></div></div>
          <div className="sm-row"><div className="sm-k">{t.share}</div><div className="sm-v"><Pill ok={cur.share} /></div></div>
          <div className="sm-row"><div className="sm-k">{t.perf}</div><div className="sm-v">{cur.perf}</div></div>
          <div className="sm-row"><div className="sm-k">{t.use}</div><div className="sm-v">{cur.use}</div></div>
        </div>
        <div className="sm-note">{cur.note}</div>
      </div>
    </div>;
};

`Docker` `Volume` `bind mount`

Delete a container and whatever you wrote into its writable layer is gone. To keep data, put it outside the container. This page makes the four destinations clear. Toggle the four modes below to see where data actually lands and whether it survives deletion:

<StorageMounts lang="en" />

## Without a mount, data is in the container writable layer

A container has a writable layer on top of the read-only image layers. Without a mount, files you write land there, using **copy-on-write** (CoW): editing an existing file copies the **whole file** from the read-only layer up to the writable layer first, then edits the copy.

* Changing one byte of a 1 GB file still copies the whole file first, so **write-heavy work is noticeably slow**.
* The writable layer follows the container lifecycle; `docker rm` wipes it all.

So databases, data you need to keep, and high-I/O workloads should never live only in the writable layer.

## Comparing the three mount types

| Dimension                   | Volume                                       | Bind mount                            | tmpfs                                |
| --------------------------- | -------------------------------------------- | ------------------------------------- | ------------------------------------ |
| Definition                  | Docker-managed named storage                 | A host path mapped into the container | Stored in host memory, never on disk |
| Where data lives            | `/var/lib/docker/volumes/<name>/_data`       | A host path you specify               | Host RAM                             |
| Survives container rm?      | Yes                                          | Yes (on the host)                     | No                                   |
| Shareable across containers | Yes                                          | Yes                                   | No                                   |
| Performance (Linux)         | Same as native host FS                       | Same as native                        | Fastest (RAM I/O)                    |
| Best for                    | Databases, persistent data, backup/migration | Dev source sync, config injection     | Secrets, scratch cache (Linux only)  |

## A named volume's physical location

On Linux:

```bash theme={null}
docker volume inspect my-vol
# "Mountpoint": "/var/lib/docker/volumes/my-vol/_data"
```

Under the WSL 2 backend, that path is inside the **`docker-desktop-data`** WSL VM, **not on the Windows filesystem**, so you cannot browse into it from File Explorer. To read volume contents:

```bash theme={null}
docker run --rm -v my-vol:/data alpine ls /data    # mount it into a container to look
```

## bind mount: `-v` vs `--mount`

Two syntaxes for the same thing, but `--mount` is explicit and officially preferred:

```bash theme={null}
# -v: short, but ambiguous
docker run -v /host/data:/app/data:ro app

# --mount: explicit type, no ambiguity
docker run --mount type=bind,source=/host/data,target=/app/data,readonly app
```

| Behavior                 | `--mount type=bind`     | `-v`                                      |
| ------------------------ | ----------------------- | ----------------------------------------- |
| Host path missing        | Errors, refuses         | Creates an empty directory                |
| Relative path (`./data`) | Treated as a bind mount | **May be treated as a named volume name** |

<Warning>
  **The `-v` relative-path trap**: `-v mydata:/app/data` (no leading slash) is treated as "create a named volume called `mydata`", not a bind mount. To be sure it is a bind mount, use an absolute path or the explicit `--mount type=bind`.
</Warning>

## Anonymous volumes and the Dockerfile `VOLUME` trap

`-v` with only a container path and no name creates a random-UUID **anonymous volume**. The Dockerfile `VOLUME` instruction also auto-creates an anonymous volume mounted at that path on `docker run`.

```dockerfile theme={null}
FROM node:20
WORKDIR /app
COPY . .
RUN npm install     # node_modules goes into the image layer
VOLUME /app         # trap: at run time an anonymous volume mounts over /app, shadowing node_modules
```

At `docker run` an anonymous volume mounts over `/app`, **shadowing the `node_modules` the image layer installed**, and the app will not start. Fix: do not declare `VOLUME` on a path that gets shadowed, or skip `VOLUME` and mount explicitly at `docker run` / Compose.

A container deleted without `--rm` leaves a dangling anonymous volume; clear it with `docker volume prune`.

## docker volume commands

```bash theme={null}
docker volume create my-vol
docker volume ls
docker volume inspect my-vol      # see the Mountpoint
docker volume rm my-vol
docker volume prune               # clear unused volumes (anonymous only by default)
```

## WSL 2 mount performance (important)

High-I/O data (source code, `node_modules`, database files) should sit on the **WSL 2 Linux filesystem** and be bind-mounted from there. **Do not mount from `/mnt/c` (Windows paths)**:

```bash theme={null}
docker run -v ~/my-project:/sources app          # correct: WSL 2 Linux filesystem
docker run -v /mnt/c/Users/me/project:/sources app   # avoid: cross-OS boundary, slow
```

Official guidance: bind mounts from the Windows filesystem are noticeably slower, and **inotify events only work on the Linux filesystem**, so mounting `/mnt/c` breaks hot reload (Vite, webpack, nodemon) because file-change events never fire. For high-I/O cases, prefer named volumes or bind mounts originating on the WSL 2 Linux side.

## Next

* [Performance settings](/en/notes/docker/guide/config/): checking and moving the `docker-desktop-data` VHDX.
* [Docker Compose](/en/notes/docker/guide/compose/): mounting volumes and bind mounts in compose.yaml.

Reference: [docs.docker.com/engine/storage](https://docs.docker.com/engine/storage/)
