> ## 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.

# Docker CLI 常用指令

> 容器生命週期、docker run 旗標、查看與除錯、映像檔指令與清理，Docker CLI 的完整常用指令參考。

export const TerminalDemo = ({lang = "zh", title, prompt, commands}) => {
  const L = ({
    zh: {
      play: "播放",
      pause: "暫停",
      step: "下一步",
      replay: "重新播放",
      reset: "重設",
      winTitle: "Windows PowerShell",
      hint: "點播放，逐行看驗證流程實際跑出來的樣子"
    },
    en: {
      play: "Play",
      pause: "Pause",
      step: "Step",
      replay: "Replay",
      reset: "Reset",
      winTitle: "Windows PowerShell",
      hint: "Press play to watch the verification run line by line"
    }
  })[lang] || ({});
  const defaultCmds = [{
    cmd: "nvidia-smi",
    output: ["Mon Jun  8 14:30:12 2026", "+-----------------------------------------------------------------------------+", "| NVIDIA-SMI 552.22       Driver Version: 552.22       CUDA Version: 12.4     |", "|-------------------------------+----------------------+----------------------+", "| GPU  Name           TCC/WDDM  | Bus-Id        Disp.A | Volatile Uncorr. ECC |", "| Fan  Temp Perf Pwr:Usage/Cap  | Memory-Usage         | GPU-Util  Compute M. |", "|===============================+======================+======================|", "|   0  GeForce RTX 3080  WDDM   | 00000000:01:00.0  On |                  N/A |", "| 30%   42C  P8    21W / 320W   |    842MiB / 10240MiB |      2%      Default |", "+-------------------------------+----------------------+----------------------+"]
  }, {
    cmd: "nvcc -V",
    output: ["nvcc: NVIDIA (R) Cuda compiler driver", "Copyright (c) 2005-2024 NVIDIA Corporation", "Built on Tue_Feb_27_16:28:36_2024", "Cuda compilation tools, release 12.4, V12.4.99", "Build cuda_12.4.r12.4/compiler.34097967_0"]
  }, {
    cmd: "python gpu_test.py",
    output: ["========================================", "PyTorch Version: 2.6.0+cu124", "CUDA Available:  True", "Device Name:     NVIDIA GeForce RTX 3080", "CUDA Version:    12.4", "✓ cuDNN Test Passed! Convolution executed successfully.", "========================================"]
  }];
  const raw = commands && commands.length ? commands : defaultCmds;
  const cmds = raw.map(c => ({
    cmd: c.cmd,
    lines: Array.isArray(c.output) ? c.output : String(c.output || "").split("\n")
  }));
  const PROMPT = prompt || "PS C:\\Users\\dev>";
  const TITLE = title || L.winTitle;
  const [idx, setIdx] = useState(0);
  const [typed, setTyped] = useState(0);
  const [outN, setOutN] = useState(0);
  const [stage, setStage] = useState("cmd");
  const [running, setRunning] = useState(false);
  const [done, setDone] = useState(false);
  useEffect(() => {
    if (!running || done) return;
    const cur = cmds[idx];
    if (!cur) {
      setRunning(false);
      setDone(true);
      return;
    }
    if (stage === "cmd") {
      if (typed < cur.cmd.length) {
        const id = setTimeout(() => setTyped(typed + 1), 28);
        return () => clearTimeout(id);
      }
      const id = setTimeout(() => setStage("out"), 200);
      return () => clearTimeout(id);
    }
    if (stage === "out") {
      if (outN < cur.lines.length) {
        const id = setTimeout(() => setOutN(outN + 1), 70);
        return () => clearTimeout(id);
      }
      const id = setTimeout(() => {
        if (idx + 1 < cmds.length) {
          setIdx(idx + 1);
          setTyped(0);
          setOutN(0);
          setStage("cmd");
        } else {
          setRunning(false);
          setDone(true);
        }
      }, 360);
      return () => clearTimeout(id);
    }
  }, [running, done, idx, typed, outN, stage]);
  const reset = () => {
    setRunning(false);
    setDone(false);
    setIdx(0);
    setTyped(0);
    setOutN(0);
    setStage("cmd");
  };
  const playPause = () => {
    if (done) {
      reset();
      setRunning(true);
      return;
    }
    setRunning(!running);
  };
  const step = () => {
    setRunning(false);
    if (done) return;
    if (idx + 1 < cmds.length) {
      setIdx(idx + 1);
      setTyped(0);
      setOutN(0);
      setStage("cmd");
    } else {
      const cur = cmds[idx];
      setTyped(cur.cmd.length);
      setOutN(cur.lines.length);
      setStage("out");
      setDone(true);
    }
  };
  const css = `
.td-root{--bg:#f4eee4;--bd:#e2d7c6;--ink:#5b5048;--accent:#bf7551;--accent-l:#cf8a68;
  --scr:#1c1815;--scr-fg:#e9e1d4;--scr-prompt:#cf8a68;--scr-dim:#8d8478;--scr-ok:#8fb573;--scr-err:#d98a72;
  border:1px solid var(--bd);border-radius:14px;overflow:hidden;background:var(--bg);
  font-family:ui-sans-serif,system-ui,"Noto Sans TC",sans-serif;margin:1.25rem 0;box-shadow:0 1px 2px rgba(60,40,25,.06);}
.dark .td-root{--bg:#26221e;--bd:#3a332c;--ink:#cabfb2;}
.td-bar{display:flex;align-items:center;gap:.6rem;padding:.55rem .8rem;background:linear-gradient(var(--bg),color-mix(in srgb,var(--bg) 90%,#000));border-bottom:1px solid var(--bd);}
.td-dots{display:flex;gap:.4rem;}
.td-dots i{width:11px;height:11px;border-radius:50%;display:block;}
.td-dots i:nth-child(1){background:#d98a72;} .td-dots i:nth-child(2){background:#dcb46a;} .td-dots i:nth-child(3){background:#8fb573;}
.td-title{font-size:.78rem;color:var(--ink);font-weight:600;letter-spacing:.01em;flex:1;text-align:center;opacity:.85;}
.td-ctrl{display:flex;align-items:center;gap:.3rem;}
.td-btn{display:inline-flex;align-items:center;gap:.32rem;border:1px solid var(--bd);background:transparent;color:var(--ink);
  border-radius:8px;padding:.3rem .55rem;font-size:.74rem;font-weight:600;cursor:pointer;transition:all .15s ease;line-height:1;}
.td-btn:hover{border-color:var(--accent);color:var(--accent);background:color-mix(in srgb,var(--accent) 10%,transparent);}
.td-btn svg{width:13px;height:13px;}
.td-btn--primary{background:var(--accent);border-color:var(--accent);color:#fff;}
.dark .td-btn--primary{background:var(--accent-l);border-color:var(--accent-l);}
.td-btn--primary:hover{background:var(--accent-l);color:#fff;}
.td-count{font-size:.72rem;color:var(--ink);opacity:.6;font-variant-numeric:tabular-nums;margin-left:.2rem;}
.td-screen{background:var(--scr);color:var(--scr-fg);padding:.85rem 1rem 1.05rem;font-family:ui-monospace,"Cascadia Code","Consolas",monospace;
  font-size:.8rem;line-height:1.55;overflow-x:auto;min-height:120px;}
.td-block{margin-bottom:.5rem;}
.td-cmdline{white-space:pre;}
.td-prompt{color:var(--scr-prompt);font-weight:600;}
.td-cmd{color:#f3ecdf;}
.td-out{white-space:pre;color:var(--scr-fg);opacity:.92;}
.td-out.ok{color:var(--scr-ok);font-weight:600;}
.td-out.err{color:var(--scr-err);font-weight:600;}
.td-cursor{display:inline-block;width:8px;height:1em;background:var(--scr-prompt);margin-left:1px;vertical-align:text-bottom;
  animation:tdblink 1s steps(2,start) infinite;}
@keyframes tdblink{to{opacity:0;}}
.td-hint{padding:.5rem .9rem;font-size:.72rem;color:var(--ink);opacity:.6;border-top:1px solid var(--bd);background:var(--bg);}
@media (max-width:600px){.td-screen{font-size:.68rem;} .td-title{display:none;} .td-btn{padding:.28rem .42rem;}}
`;
  const PlayIcon = () => <svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M7 5.5v13l11-6.5z" /></svg>;
  const PauseIcon = () => <svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><rect x="6.5" y="5" width="4" height="14" rx="1" /><rect x="13.5" y="5" width="4" height="14" rx="1" /></svg>;
  const StepIcon = () => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 4l10 8-10 8z" /><line x1="19" y1="5" x2="19" y2="19" /></svg>;
  const ResetIcon = () => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 12a9 9 0 1 0 3-6.7L3 8" /><path d="M3 3v5h5" /></svg>;
  const shown = Math.min(idx + (done ? 1 : 0), cmds.length);
  return <div className="td-root">
      <style>{css}</style>
      <div className="td-bar">
        <span className="td-dots"><i /><i /><i /></span>
        <span className="td-title">{TITLE}</span>
        <span className="td-ctrl">
          <button className="td-btn td-btn--primary" onClick={playPause}>
            {running ? <PauseIcon /> : <PlayIcon />}
            {done ? L.replay : running ? L.pause : L.play}
          </button>
          <button className="td-btn" onClick={step} disabled={done}><StepIcon />{L.step}</button>
          <button className="td-btn" onClick={reset}><ResetIcon />{L.reset}</button>
          <span className="td-count">{shown}/{cmds.length}</span>
        </span>
      </div>
      <div className="td-screen">
        {Array.from({
    length: idx + 1
  }).map((_, i) => {
    const c = cmds[i];
    if (!c) return null;
    const isCur = i === idx;
    const cmdText = isCur ? c.cmd.slice(0, typed) : c.cmd;
    const lines = isCur ? c.lines.slice(0, outN) : c.lines;
    const cursor = isCur && !done && stage === "cmd";
    return <div className="td-block" key={i}>
              <div className="td-cmdline">
                <span className="td-prompt">{PROMPT}</span> <span className="td-cmd">{cmdText}</span>
                {cursor && <span className="td-cursor" />}
              </div>
              {lines.map((ln, j) => {
      const tr = ln.trim();
      const cls = tr.startsWith("✓") ? "ok" : tr.startsWith("✗") || tr.startsWith("✘") ? "err" : "";
      return <div className={"td-out " + cls} key={j}>{ln.length ? ln : " "}</div>;
    })}
            </div>;
  })}
      </div>
      <div className="td-hint">{L.hint}</div>
    </div>;
};

`Docker` `CLI`

每天真正會打的指令都在這。以 Docker 官方 CLI 參考為準（截至 2026-06）。觀念基礎見 [核心概念](/notes/docker/guide/concepts/)。

## 容器生命週期

| 指令                         | 用途                          | 範例                                      |
| -------------------------- | --------------------------- | --------------------------------------- |
| `docker run`               | 從映像建立並啟動新容器                 | `docker run -d --name web nginx:alpine` |
| `docker start`             | 啟動已停止的容器                    | `docker start web`                      |
| `docker stop`              | 送 SIGTERM 優雅停止（預設等 10 秒寬限期） | `docker stop web`                       |
| `docker restart`           | 停止後重新啟動                     | `docker restart web`                    |
| `docker pause` / `unpause` | 凍結 / 解凍容器內所有行程              | `docker pause web`                      |
| `docker rm`                | 刪除已停止的容器（`-f` 強制刪執行中）       | `docker rm web`                         |

```bash theme={null}
docker run -d --name web nginx:alpine   # 建立並背景啟動
docker stop -t 0 web                     # 不等 SIGTERM 直接停
docker rm -f web                         # 強制刪除執行中的容器
```

## `docker run` 重要旗標

旗標決定容器怎麼跑，這些最常用：

| 旗標                    | 用途                                  | 範例                                     |
| --------------------- | ----------------------------------- | -------------------------------------- |
| `-d`                  | 背景執行（detached），只印 container ID      | `docker run -d nginx:alpine`           |
| `-it`                 | 互動式 shell（`-i` 保留 STDIN、`-t` 配 TTY） | `docker run -it ubuntu bash`           |
| `--name`              | 指定容器名稱（同一主機須唯一）                     | `docker run --name web nginx`          |
| `--rm`                | 容器結束後自動刪除（含匿名 volume）               | `docker run --rm alpine echo hi`       |
| `-p`                  | 發布埠，格式 `主機:容器`                      | `docker run -p 8080:80 nginx`          |
| `--network`           | 連到指定網路                              | `docker run --network mynet app`       |
| `-v` / `--mount`      | 掛載 volume 或 bind mount              | `docker run -v data:/app/data app`     |
| `-e` / `--env-file`   | 設環境變數 / 從檔案讀                        | `docker run -e TZ=Asia/Taipei app`     |
| `--restart`           | 重啟策略（見下）                            | `docker run --restart=always nginx`    |
| `--gpus`              | 傳入 GPU（需 NVIDIA Container Toolkit）  | `docker run --gpus all app nvidia-smi` |
| `-w` / `-u`           | 工作目錄 / 執行身分                         | `docker run -w /app -u 1000:1000 app`  |
| `--memory` / `--cpus` | 資源上限                                | `docker run -m 512m --cpus 1.5 app`    |

`--restart` 四種策略：

| 值                | 行為                                              |
| ---------------- | ----------------------------------------------- |
| `no`（預設）         | 不自動重啟                                           |
| `on-failure[:N]` | 只有非零退出碼才重啟，可設最多重試 N 次                           |
| `always`         | 一律重啟，daemon 重啟後也拉起來                             |
| `unless-stopped` | 同 `always`，但你手動 `docker stop` 後，daemon 重啟不會自動拉起 |

<Warning>
  `-p` 的方向是 `主機:容器`，左邊是你的電腦、右邊是容器內部。`-p 8080:80` 是「主機 8080 轉進容器 80」，寫反了會連不到。只開給本機可加 host IP：`-p 127.0.0.1:8080:80`。
</Warning>

## 查看與除錯

| 指令               | 用途                                  |
| ---------------- | ----------------------------------- |
| `docker ps`      | 列出執行中容器（`-a` 含已停止、`-q` 只印 ID）       |
| `docker logs`    | 看容器日誌（`-f` 即時、`--tail N`、`--since`） |
| `docker exec`    | 在執行中的容器裡跑指令（`-it` 進互動 shell）        |
| `docker inspect` | 看容器 / 映像完整 JSON（IP、掛載、環境變數）         |
| `docker stats`   | 即時看 CPU / 記憶體 / 網路 / Block I/O      |
| `docker top`     | 看容器內執行中的行程                          |
| `docker cp`      | 在容器與主機之間複製檔案                        |

```bash theme={null}
docker ps -a -q --filter status=exited        # 所有已停止容器的 ID
docker logs -f --tail 100 web                  # 即時看最後 100 行
docker exec -it -u root web bash               # 以 root 進互動 shell
docker inspect --format='{{.NetworkSettings.IPAddress}}' web   # 取容器 IP
docker stats --no-stream                       # 取一次快照不持續串流
docker cp web:/app/logs/app.log ./app.log      # 容器檔案複製到主機
```

一個「跑起來、看狀態、進去看、收掉」的流程跑出來長這樣：

<TerminalDemo
  lang="zh"
  title="Ubuntu (WSL 2)"
  prompt="user@wsl:~$"
  commands={[
{
  cmd: "docker run -d --name web -p 8080:80 nginx:alpine",
  output: [
    "Unable to find image 'nginx:alpine' locally",
    "alpine: Pulling from library/nginx",
    "b0a0cf830b12: Pull complete",
    "Status: Downloaded newer image for nginx:alpine",
    "9f3c2e1a4b7d8c0e1f2a3b4c5d6e7f80",
  ],
},
{
  cmd: "docker ps",
  output: [
    "CONTAINER ID   IMAGE          STATUS         PORTS                    NAMES",
    "9f3c2e1a4b7d   nginx:alpine   Up 3 seconds   0.0.0.0:8080->80/tcp     web",
  ],
},
{
  cmd: "docker exec web nginx -v",
  output: [
    "nginx version: nginx/1.27.3",
  ],
},
{
  cmd: "docker stop web && docker rm web",
  output: ["web", "web"],
},
]}
/>

## 映像檔指令

| 指令                     | 用途                                 |
| ---------------------- | ---------------------------------- |
| `docker images`        | 列出本機所有映像（`-a` 含中間層）                |
| `docker pull`          | 從 registry 拉映像                     |
| `docker build`         | 從 Dockerfile 建映像（`-t` 命名、`-f` 指定檔） |
| `docker tag`           | 替映像加新 tag                          |
| `docker rmi`           | 刪除映像（`-f` 強制）                      |
| `docker history`       | 看映像每層的建置指令與大小                      |
| `docker save` / `load` | 匯出成 tar / 匯入（離線搬運）                 |

```bash theme={null}
docker pull nginx:1.27-alpine
docker build -f docker/Dockerfile.prod -t myapp:prod .
docker tag myapp:1.0 user/myapp:1.0
docker save myapp:1.0 | gzip > myapp.tar.gz     # 壓縮匯出
docker load < myapp.tar.gz                       # 匯入
```

`docker build` 的完整用法在 [從零搭建 Dockerfile](/notes/docker/dockerfile/basics/)。

## 清理指令

映像、容器、volume 會越積越多。**這組指令有破壞性，分清楚各自清什麼：**

```bash theme={null}
docker system df                      # 先看磁碟用量與可回收量
docker system df -v                   # 每個物件的細項

docker container prune                # 清所有已停止容器
docker image prune                    # 只清懸空（dangling）映像
docker image prune -a                 # 連所有沒被容器引用的映像（含 tag）
docker volume prune                   # 只清匿名 volume
docker network prune                  # 清未使用的自訂網路

docker system prune                   # 停止容器 + 未用網路 + dangling 映像 + build cache
docker system prune -a                # 映像擴大到「所有沒被容器引用的」
docker system prune -a --volumes      # 再加匿名 volume，破壞性最強
```

<Warning>
  **清理不可逆，下手前先 `docker system df`：**

  * `docker image prune -a` / `system prune -a` 會刪所有沒被容器使用的映像，包括你之後還想用的版本，得重新 pull。
  * `--volumes` 只清**匿名** volume；要清具名 volume（資料庫資料常在這）得用 `docker volume prune -a`，**資料無法復原**。
  * 系統內建的 `bridge` / `host` / `none` 網路永遠不會被 prune。
</Warning>

## 多 daemon 與診斷

```bash theme={null}
docker context ls                                    # 列出可連線的 Docker daemon
docker context create remote --docker "host=ssh://user@10.0.0.5"
docker --context remote ps                           # 對遠端 daemon 跑單一指令
docker version                                       # client 與 daemon 版本、API 版本
docker info                                          # 整個安裝的系統資訊
```

## 已棄用項目（截至 2026-06）

| 舊用法                         | 現狀               | 取代                                 |
| --------------------------- | ---------------- | ---------------------------------- |
| `docker-compose`（v1，Python） | 已 EOL、不再維護       | `docker compose`（v2，內建 CLI plugin） |
| `docker run --link`         | 已棄用              | user-defined network + DNS 名稱解析    |
| `DOCKER_BUILDKIT=0`         | 可停 BuildKit 但不建議 | 直接用 BuildKit（Docker 23+ 預設）        |

## 接下來

* [Docker Hub](/notes/docker/guide/hub/)：pull / push 的映像從哪來。
* [效能設定](/notes/docker/guide/config/)：WSL 2 後端資源與 daemon.json。
* [資料落點](/notes/docker/guide/storage/)：`-v` 與 `--mount` 的差別與雷。
* [Docker Compose](/notes/docker/guide/compose/)：多容器一次拉起。

官方參考：[docs.docker.com/reference/cli/docker](https://docs.docker.com/reference/cli/docker/)
