目錄

清理 Docker 佔用的硬碟空間(prune 指令)

用 Docker 一段時間後,舊的映像檔、停止的容器、建置快取、沒人用的 Volume 會越積越多,動輒佔用數十 GB。這一節先找出空間被誰吃掉,再安全地清理。所有輸出都是在同一個環境中依序執行的結果。

先看看用了多少

$ docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          13        3         2.886GB   2.377GB (82%)
Containers      4         1         98.3kB    16.38kB (16%)
Local Volumes   4         1         40.61MB   40.61MB (100%)
Build Cache     151       0         3.554GB   2.954GB
欄位意思
TOTAL總數
ACTIVE正在被容器使用的數量
SIZE佔用的空間
RECLAIMABLE可以清掉的部分(沒被使用的)

這個環境有 13 個映像檔,只有 3 個在使用,2.377 GB(82%)可以回收;建置快取更大,有 2.954 GB。加上 -v 可以看到每個映像檔、容器、Volume 的明細:docker system df -v。

目前的映像檔和容器:

$ docker images
IMAGE               ID             DISK USAGE   CONTENT SIZE   EXTRA
adminer:latest      74f29c416e14        164MB         43.8MB
alpine:3.23         85fe1e81d675       26.6MB         8.21MB   U
hello-node:latest   4063457524bb        245MB         62.2MB
myapp:latest        80cb5c41a286       54.9MB         24.8MB   U
mysql:8.4           24989179f588       1.12GB          254MB
nginx:1.30          9bf97bd7714f        242MB         66.5MB
nginx:1.30-alpine   0985e772fb9f       93.7MB           27MB   U
node:24-alpine      ebfe2f904627        242MB           62MB
postgres:18         74935e722416        650MB          169MB
python:3.14-slim    a2b82f3c4855        180MB         45.7MB
redis:8-alpine      3811787313eb        161MB         39.6MB
registry:3          ddf754342cfc       86.9MB         20.7MB
ubuntu:26.04        f144425ff09b        162MB         45.6MB
$ docker ps -a
CONTAINER ID   IMAGE               COMMAND                  CREATED         STATUS                     PORTS     NAMES
cc43746c9a29   alpine:3.23         "true"                   7 seconds ago   Exited (0) 6 seconds ago             anon1
7e269fff858b   nginx:1.30-alpine   "/docker-entrypoint.…"   8 seconds ago   Up 7 seconds               80/tcp    web
bf5ff318eb63   alpine:3.23         "echo done"              8 seconds ago   Exited (0) 7 seconds ago             old2
b580380c1fd2   myapp               "cat /version.txt"       9 seconds ago   Exited (0) 8 seconds ago             old1

EXTRA 欄的 U 表示 In Use(有容器在使用)。容器中只有 web 在執行,其他三個都已經結束(Exited),但仍然佔用空間。

分項清理

指令清除的對象
docker container prune所有已停止的容器
docker image prune懸空映像檔(沒有名稱的 <none>)
docker image prune -a所有沒有被任何容器使用的映像檔
docker builder prune建置快取
docker volume prune未被使用的匿名 Volume
docker volume prune -a未被使用的所有 Volume(含具名的)
docker network prune未被使用的自訂網路

每個指令都會先要求確認(Are you sure you want to continue? [y/N]),加 -f 可以略過確認,適合寫在腳本裡。以下範例都加了 -f。

刪除已停止的容器

$ docker container prune -f
Deleted Containers:
cc43746c9a2993bda0fb21ab3f161b78603353714f396f86fa8c4b5caf90e25a
bf5ff318eb6331a05239ad7fa39420ff96dc1c8f84b9a3548ce38a0f71a2d63a
b580380c1fd2dc6cca2dc6e009bb877c3eb2df1cf388664a32c8932aff8169fa

Total reclaimed space: 16.38kB

三個已停止的容器被刪除,執行中的 web 不受影響。

如果使用 Docker Desktop,也可以在 Containers 畫面勾選容器後按 Delete:

Docker Desktop 的 Containers 畫面勾選容器後按 Delete
Docker Desktop:勾選容器後按 Delete。圖片來源:Docker 官方文件(Apache-2.0)

Docker 29:重新建置不再留下 <none> 映像檔

以前用同一個名稱重新建置映像檔(例如 docker build -t myapp . 執行多次),舊版本會變成名稱是 <none> 的「懸空映像檔」,需要用 docker image prune 清理。實測 Docker 29:

$ docker build -q --build-arg VERSION=4 -t myapp:latest .
sha256:3f33bc33eb36171280e9b90d904e25c58b2dcf509390536580ffacafc35d4595

$ docker images --filter dangling=true
IMAGE   ID             DISK USAGE   CONTENT SIZE   EXTRA

$ docker ps -a --filter name=old1
CONTAINER ID   IMAGE          COMMAND              CREATED          STATUS                      PORTS     NAMES
b580380c1fd2   80cb5c41a286   "cat /version.txt"   26 seconds ago   Exited (0) 26 seconds ago             old1

連續建置了 4 次 myapp:latest,--filter dangling=true 卻是空的。Docker 29 預設使用 containerd 映像檔儲存,舊版本在名稱被新映像檔取代時就不再保留成 <none>。仍在使用舊版本的容器(old1),IMAGE 欄會改為顯示映像檔 ID。

所以在 Docker 29 上,docker image prune(不加 -a)通常沒什麼可以清,真正佔空間的是沒被使用但還有名稱的映像檔,以及建置快取。

刪除匿名 Volume

$ docker volume ls
DRIVER    VOLUME NAME
local     6f6f8ce590c609d4f39b3a676282da88866c8ab20a064132f7746c3cf6717f2c
local     991c93cb6ebeac8570e9c15a89b4225c2bb5e20cbfde2f5bf665fa4dfa23a1aa
local     5396795eeeaab8415295814207a02f75f6ea136d5c34574ba9b16ecdc1b975f9
local     cleanup-demo_pgdata

$ docker volume prune -f
Deleted Volumes:
5396795eeeaab8415295814207a02f75f6ea136d5c34574ba9b16ecdc1b975f9
991c93cb6ebeac8570e9c15a89b4225c2bb5e20cbfde2f5bf665fa4dfa23a1aa
6f6f8ce590c609d4f39b3a676282da88866c8ab20a064132f7746c3cf6717f2c

Total reclaimed space: 0B

$ docker volume ls
DRIVER    VOLUME NAME
local     cleanup-demo_pgdata
  • 前三個長串名稱的是匿名 Volume。例如 postgres 映像檔的 Dockerfile 宣告了 VOLUME,即使執行時沒有掛載,Docker 也會自動建立匿名 Volume,容器刪除後它還留著(參考 Volume 與資料保存)。
  • docker volume prune 只刪除沒被使用的匿名 Volume。
  • cleanup-demo_pgdata 是 Compose 專案的具名 Volume,雖然 docker compose down 之後已經沒有容器在使用,但沒有被刪除。

刪除建置快取

$ docker builder du | tail -n 3
Private:	2.996GB
Reclaimable:	3.596GB
Total:		3.596GB

$ docker builder prune -f --filter until=1h | tail -n 3
lv85kuv5xx5h65k941qjoxecz               	true 	98.69kB   	About an hour ago
kc9tcgw8l1aq9dz1agx096se0               	true 	870kB     	About an hour ago
Total:	1.662GB

$ docker builder prune -f | tail -n 3
6os07ml167zpcfh09ushitk2b               	true 	8.285kB   	38 minutes ago
7ir7u28j6esk2x7pyu6q38kvk               	true 	74.75MB   	38 minutes ago
Total:	1.334GB
  • docker builder du 顯示建置快取總共 3.596 GB,全部可回收。
  • --filter until=1h 只刪除超過 1 小時沒有使用的快取,清掉了 1.662 GB。
  • 不加篩選則再清掉 1.334 GB。

刪除建置快取的代價是下次建置要重新執行所有步驟(例如重新 npm ci、pip install),會比較慢,但不會影響任何映像檔或容器。

一次清理:docker system prune

docker system prune

會刪除:已停止的容器、未使用的網路、懸空映像檔、建置快取。

docker system prune -a

再加上所有未被使用的映像檔,下次用到時要重新下載。

實測:--volumes 會刪除資料庫的資料嗎?

很多教學(包括本教學的舊版)警告:docker system prune --volumes 會把 docker compose down 之後「暫時沒人用」的資料庫 Volume 也刪掉。在 Docker 29 實測:

$ docker system prune --volumes -f
Deleted Containers:
871f1c408d23dd413804105498b5520385dfafc9e558c02447ed940684bbc483

Deleted Volumes:
e5d0caa972c5323d5a64e9baf5fcb21d66ce7fbb4879bdcc7aa068a14fdbbf76

Total reclaimed space: 8.192kB

$ docker volume ls
DRIVER    VOLUME NAME
local     cleanup-demo_pgdata

--volumes 只刪除了一個匿名 Volume,Compose 資料庫的具名 Volume cleanup-demo_pgdata 還在。從 Docker 23 開始,volume prune 和 system prune --volumes 預設只處理匿名 Volume。

真正會刪除具名 Volume 的是 docker volume prune -a:

$ docker volume prune -a -f
Deleted Volumes:
cleanup-demo_pgdata

Total reclaimed space: 40.61MB

$ docker volume ls
DRIVER    VOLUME NAME

這次 cleanup-demo_pgdata 被刪除了,裡面的 40.61 MB 資料庫檔案也一起消失,無法復原。

只有在確定不需要任何資料時,才使用 docker volume prune -a。要刪除特定的 Volume,用 docker volume rm 名稱 比較安全;Compose 專案則用 docker compose down -v,只會刪除該專案的 Volume。如果你使用的是 Docker 20.10 或更早的版本,--volumes 仍然會刪除具名 Volume。

刪除所有未使用的映像檔

$ docker image prune -a -f | grep -E "^untagged|^Total"
untagged: nginx:1.30
untagged: node:24-alpine
untagged: postgres:18
untagged: python:3.14-slim
untagged: myapp:latest
untagged: mysql:8.4
untagged: redis:8-alpine
untagged: registry:3
untagged: ubuntu:26.04
untagged: adminer:latest
untagged: alpine:3.23
untagged: hello-node:latest
Total reclaimed space: 2.665GB

$ docker images
IMAGE               ID             DISK USAGE   CONTENT SIZE   EXTRA
nginx:1.30-alpine   0985e772fb9f       93.7MB           27MB   U

$ docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          1         1         93.66MB   0B (0%)
Containers      1         1         81.92kB   0B (0%)
Local Volumes   0         0         0B        0B
Build Cache     26        0         599.6MB   506.9MB
  • 除了正在被 web 容器使用的 nginx:1.30-alpine,其他 12 個映像檔都被刪除,回收了 2.665 GB。
  • 最後的 docker system df 顯示映像檔只剩 93.66 MB。

這些映像檔下次 docker run 或建置時會自動重新下載。網路慢、或映像檔很大(例如 AI 模型相關的映像檔動輒數 GB)時,請考慮用 --filter 只刪除舊的:

docker image prune -a --filter "until=168h"      # 只刪除 7 天前建立的

注意 until 比對的是映像檔的建立時間(映像檔作者建置的時間),不是你下載的時間。

容器日誌也會吃空間

預設的 json-file 日誌沒有大小上限。實測一個不斷輸出的容器(yes 指令每秒輸出大量文字):

$ docker run -d --name noisy alpine:3.23 sh -c "timeout 3 yes hello-docker-log; sleep 600"
863ccacbc4bcf86a81cc3bf7603cef1d6ecc004553ac4b36dd7ef52172ed3dc6

$ docker inspect --format "{{.LogPath}}" noisy
/var/lib/docker/containers/863ccacbc4bcf86a81cc3bf7603cef1d6ecc004553ac4b36dd7ef52172ed3dc6/863ccacbc4bcf86a81cc3bf7603cef1d6ecc004553ac4b36dd7ef52172ed3dc6-json.log

$ du -h $(docker inspect --format "{{.LogPath}}" noisy)
69.3M	/var/lib/docker/containers/863ccacbc4bcf86a81cc3bf7603cef1d6ecc004553ac4b36dd7ef52172ed3dc6/863ccacbc4bcf86a81cc3bf7603cef1d6ecc004553ac4b36dd7ef52172ed3dc6-json.log

$ docker rm -f noisy
noisy

只輸出了 3 秒,日誌檔就有 69.3 MB。長期執行、日誌又多的容器,日誌檔可能長到數十 GB,而且不會自動刪除,直到容器被刪除為止。日誌檔位於 /var/lib/docker/containers/容器ID/ 中。在 Linux 主機上可以這樣找出最大的日誌檔:

sudo du -h /var/lib/docker/containers/*/*-json.log | sort -h | tail

單一容器設定上限

$ docker run -d --name noisy --log-opt max-size=10m --log-opt max-file=3 alpine:3.23 sh -c "timeout 3 yes hello-docker-log; sleep 600"
fab1c34ceecb955dc33c01f0e7aa0a60b4137e68d0fe465d52ce23b49ba66161

$ ls -lh $(dirname $(docker inspect --format "{{.LogPath}}" noisy)) | grep json
-rw-------    1 root     root        3.2K Oct 13 01:07 config.v2.json
-rw-r-----    1 root     root        7.2M Oct 13 01:07 fab1c34ceecb955dc33c01f0e7aa0a60b4137e68d0fe465d52ce23b49ba66161-json.log
-rw-r-----    1 root     root        9.5M Oct 13 01:07 fab1c34ceecb955dc33c01f0e7aa0a60b4137e68d0fe465d52ce23b49ba66161-json.log.1
-rw-r-----    1 root     root        9.5M Oct 13 01:07 fab1c34ceecb955dc33c01f0e7aa0a60b4137e68d0fe465d52ce23b49ba66161-json.log.2
-rw-------    1 root     root        1.5K Oct 13 01:07 hostconfig.json

$ docker inspect --format '{{json .HostConfig.LogConfig}}' noisy
{"Type":"json-file","Config":{"max-file":"3","max-size":"10m"}}

$ docker rm -f noisy
noisy

max-size=10m 表示每個檔案最大 10 MB,max-file=3 表示最多保留 3 個檔案,舊的會被輪替成 .1、.2。同樣的輸出量,日誌總共只佔約 26 MB,而且之後也不會再增加。

Compose 的寫法:

services:
  app:
    image: myapp
    logging:
      options:
        max-size: "10m"
        max-file: "3"

設定全域日誌上限

讓所有新容器都套用上限:Linux 編輯 /etc/docker/daemon.json,Docker Desktop 則在 Settings → Docker Engine 中編輯同樣的 JSON:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Linux 修改後執行 sudo systemctl restart docker,Docker Desktop 按 Apply & restart。只有之後新建立的容器才會套用,已存在的容器要刪除重建(Compose 用 docker compose up -d --force-recreate)。

Docker Desktop:清理後硬碟空間沒有變多

在 Windows 和 macOS 上,Docker 的所有資料存在一個虛擬磁碟檔中。刪除映像檔後,虛擬磁碟中的空間被釋放了,但這個檔案本身不一定會立即縮小。

  • Docker Desktop:Settings → Resources → Advanced 可以查看與調整虛擬磁碟的上限。Troubleshoot 畫面中的 Clean / Purge data 會清空所有 Docker 資料(映像檔、容器、Volume 全部刪除),只在最後手段時使用。
  • Windows(WSL 2):如果 .vhdx 檔仍然很大,可以先執行 wsl --shutdown,再用 diskpart 的 compact vdisk 壓縮對應的 .vhdx 檔。

建議的定期清理

在伺服器上可以用 cron 每週執行一次:

docker container prune -f --filter "until=168h"
docker image prune -af --filter "until=168h"
docker builder prune -af --filter "until=168h"

這組指令不會動到任何 Volume,資料庫的資料是安全的。再搭配全域日誌上限,硬碟空間就不容易失控。

重點回顧

  • docker system df 先看,再決定清什麼。
  • image prune -a 刪除所有沒在用的映像檔;builder prune 清建置快取。
  • volume prune 只刪匿名 Volume,-a 才包含具名的。
  • 設定日誌上限 max-size 與 max-file。

動手練習

練習 1:寫一組可以放進 cron、不會動到任何 Volume 的清理指令,只清除 7 天以上的映像檔與建置快取。

答案
docker image prune -af --filter "until=168h"
docker builder prune -af --filter "until=168h"

常見問題

docker system prune 安全嗎?

不加 --volumes 時不會動到任何 Volume;Docker 23 起即使加 --volumes 也只刪匿名 Volume。真正危險的是 docker volume prune -a。

清理後 Windows 的硬碟空間沒有變多?

Docker Desktop 的資料存在虛擬磁碟檔中,刪除映像檔後檔案不一定立即縮小,見本章最後一節。

建置快取可以全部刪掉嗎?

可以,不影響任何映像檔或容器,只是下次建置會比較慢。

更新於 2026年10月14日