Docker 占满磁盘:image、container、volume、build cache 各占多少,按什么顺序清
Docker 的磁盘占用分四类,计量口径完全不同,混在一起看只会得出"镜像很大"这种没用的结论。正确做法是先 docker system df 拿到四类的总量与可回收量,再用 -v 展开到具体对象,最后按"删除风险从低到高"的顺序清理。我本机这份盘面很典型:镜像 2.912 GB、容器 100.7 MB、本地卷 14.52 GB、构建缓存 0 B——真正的大头是没人引用的卷,不是镜像。
先看总量,再看明细
docker system df # 四类总量
docker system df -v # 展开到每个镜像/容器/卷
本机实测(Docker Desktop 29.4.3):
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 11 3 2.912GB 1.808GB (62%)
Containers 5 5 100.7MB 0B (0%)
Local Volumes 35 0 14.52GB 14.52GB (100%)
Build Cache 0 0 0B 0B
四个数字的读法不一样:
| 类别 | SIZE 的含义 | 清理命令 | 风险 |
|---|---|---|---|
| Images | 镜像虚拟大小(SHARED + UNIQUE) | docker image prune(悬空) / -a(未被引用) | -a 会删掉所有没有容器引用的镜像 |
| Containers | 容器可写层 | docker container prune | 只删已停止的容器,运行中的不受影响 |
| Local Volumes | 卷占用 | docker volume rm <name> | 卷里是数据,删了不可恢复 |
| Build Cache | 构建中间层 | docker builder prune | 下次构建要重跑未命中缓存的层 |
清理顺序:从最安全到最危险
docker container prune # 1. 已停止的容器,通常最安全
docker image prune # 2. 悬空镜像(<none>:<none>)
docker builder prune # 3. 构建缓存,可用 --filter until=24h
docker image prune -a # 4. 所有未被容器引用的镜像
docker volume rm <name> # 5. 卷,逐个确认再删
每一步之后重新跑一次 docker system df,用回收量核对判断是否正确。第 4 步之前先用 docker image ls 把将被删除的列表打出来存档,因为 docker image prune -a 的确认提示不会列出具体对象,--filter 还支持 until=、label= 等筛选条件。
三个读数陷阱
第一,删掉一个 tag 不代表能回收 SIZE 那么多。-v 里 postgres:14 是 629 MB,其中 SHARED SIZE 87.71 MB、UNIQUE SIZE 540.9 MB;postgres:18 的共享部分也是 87.71 MB。这两个镜像共享同一批基础层,删掉 14 只会回收它独占的 540.9 MB。判断能回收多少,看 UNIQUE SIZE,不要看 SIZE。
同样地,awd-player 是 840 MB,其中 633.5 MB 与 awd-player-ai-controller 共享,UNIQUE SIZE 只有 206.8 MB,而且它被 3 个容器引用(CONTAINERS 列为 3)——这一条根本不在可回收范围内。
第二,RECLAIMABLE 的百分比是相对 SIZE 算的,不是相对磁盘。本机镜像一栏 62%,指的是 2.912 GB 里有 1.808 GB 可以回收;如果你只盯着百分比,会误以为还有 38% 的"浪费"没法处理,实际那是正在被容器使用的部分。
第三,卷的 ACTIVE 0 不等于可以删。35 个卷全部显示无人引用、100% 可回收,合计 14.52 GB,看上去删起来很爽;但"没有容器引用"和"数据没用"是两件事,卷常常是数据库、上传目录、证书的落盘位置,容器临时停掉时 ACTIVE 也会是 0。删之前用 docker volume inspect 看 Labels 和创建时间,能定位到是哪个 compose 项目留下的。
构建缓存单独算
Build Cache 是独立的一类,既不算在镜像里,也不受 docker image prune 影响。本机这一栏是 0 B,因为这台机器上没有跑过 docker build——这也说明"构建缓存占满磁盘"只发生在真正构建过的机器上。清理用 docker builder prune,加 --filter until=24h 可以只清一天前的缓存,保留最近构建的热层。
清理之后仍不降,去文件系统层看
Docker 的数据目录默认在 /var/lib/docker,overlay2 的每层数据、每个容器的可写层、卷都在这里。docker system df 统计的是 Docker 自己认识的对象,不包括容器内部写出的文件被记在可写层之外的部分,也不包括日志驱动写到宿主文件系统的内容。所以"清完镜像和卷,磁盘还是满"的情况下,直接对数据目录做一次:
du -xhd1 /var/lib/docker | sort -h # 看 overlay2 / volumes / containers 哪块大
journalctl --disk-usage # 若日志驱动是 journald
du -sh /var/lib/docker/containers/*/*-json.log # json-file 驱动的容器日志
json-file 驱动的容器日志默认没有大小上限,一个刷屏的容器写到几十 GB 很常见,而它在 docker system df 里只体现为容器的可写层大小,很容易被忽略。对策是在 daemon.json 里配 log-opts 的 max-size 与 max-file,或者改用其他日志驱动。
定期清理怎么配
docker system df 只回答"现在占了多少",不负责回收。要让它长期不失控,可以给几类清理各配一条定时命令,配合 --filter until= 把"最近还在用"的对象排除掉:
docker container prune -f --filter until=24h
docker image prune -f --filter until=168h # 一周前的悬空镜像
docker builder prune -f --filter until=168h
until 接受 Go 的时长写法(24h、168h)和 RFC3339 时间戳,语义是"只处理创建时间早于该值之前的对象"。用时间过滤而不是直接 -a,能避免把刚构建好、还没来得及起容器的镜像删掉——这一点在 CI 机器上尤其重要,构建与部署之间的几分钟空窗期正好会被"无容器引用"的判定命中。
小结
四类占用要分开计量:镜像看 UNIQUE SIZE,容器看可写层,卷看引用数与业务含义,构建缓存单独清。顺序上先做无风险的三步(停止的容器、悬空镜像、构建缓存),再处理未被引用的镜像,最后才动卷。每步之后用 docker system df 复核,是这套流程里唯一能防止"清了 2 GB 但磁盘没变"的环节。
如果清完之后空间仍然紧张,问题往往不在 Docker 本身:/var/lib/docker 所在分区可能被日志、容器内写的文件、或者 overlay 的 upperdir 占满了。这时候要回到文件系统层面用 df/du 定位,而不是继续在 Docker 里删东西。
数据来源与检索时间
| 来源 | URL | 对应数据 |
|---|---|---|
| docs.docker.com · docker system df | https://docs.docker.com/reference/cli/docker/system/df/ | 四类占用的定义、-v 的 SHARED SIZE/UNIQUE SIZE/SIZE 关系、RECLAIMABLE 列含义 |
| docs.docker.com · docker image prune | https://docs.docker.com/reference/cli/docker/image/prune/ | 默认只删悬空镜像、-a 删除所有未被引用镜像、--filter until=/label= 语法、确认提示不列出具体对象 |
检索时间:2026-11-10(Asia/Shanghai)。
核验说明
- 真跑:
docker system df与docker system df -v在本机(Windows 11 + Docker Desktop 29.4.3)执行,文中所有数值(11 个镜像 / 2.912 GB / 35 个卷 / 14.52 GB / 构建缓存 0 B、postgres 两版的 SHARED 与 UNIQUE)均为原始输出。 - 真跑:
awd-player的CONTAINERS列为 3 与UNIQUE SIZE206.8 MB 来自同一次-v输出。 - 未执行:文中未运行任何
prune命令。本机 35 个卷与 11 个镜像中有一部分属于同机其他项目(ai-controller、awd-player1..3等运行中的容器),执行prune -a或--volumes会影响它们,因此清理动作只给出命令与顺序,未实际回收空间。 - 仅依据文档:
docker builder prune的--filter until=行为、docker image prune -a --filter label=的组合语义,按官方页面记录,本机无构建缓存可验证。