Docker no space left on device 磁盘爆满:清理命令从安全到激进的完整阶梯
现象:build 到一半 / pull 到一半,突然没空间了
# 构建时
failed to solve: ... write /var/lib/docker/tmp/...: no space left on device
# 拉镜像时
failed to register layer: Error processing tar file(no space left on device)
# 运行中容器写日志
Error response from daemon: ... no space left on device
一行结论:宿主机放 /var/lib/docker 的那块盘满了。Docker 用「写时复制」层叠镜像,每一次 build、pull、run 都在往同一个目录堆东西——而其中大部分是可再生成的垃圾。
这类报错也会伪装成别的症状:容器莫名 OOM 后立刻起不来、compose up 某一服务卡在 Creating、CI 机器「昨天还能跑今天全红」。先别急着加盘,按下面顺序看清楚是谁吃掉了空间。
先看清楚:空间被谁吃了
df -h /var/lib/docker # 盘还剩多少
docker system df # Docker 视角的四类占用
docker system df -v # 每个镜像/容器/卷的细账
docker system df 的输出对应四个清理维度:
| 类别 | 是什么 | 可清理性 |
|---|---|---|
| Images | 镜像(含 dangling 悬空层) | 多数可再生 |
| Containers | 停止的容器占着的可写层 | 看业务 |
| Local Cache | build cache | 可再生 |
| Volumes | 持久化数据 | ⚠️ 数据,默认不动 |
加 -v 能看到每个镜像/容器的具体体积,先定位再动手,不要上来就 prune -a。
两个人对「谁把盘打满了」争论不休时,再补两条取证命令:
# inode 与字节分开看——「有空间却报满」多半是 inode
df -i /var/lib/docker
# 真实目录体积(-x 不跨文件系统,避免挂载点把数字搅乱)
du -xh --max-depth=1 /var/lib/docker | sort -rh | head
清理阶梯:从安全到激进,逐级执行
# 第一级:只清已停止的容器(docker ps -a 里非 Up 的)
docker container prune
# 第二级:清 dangling 镜像(<none>:<none> 的悬空层,build 失败的残留)
docker image prune
# 第三级:清所有「没有被任何容器引用」的镜像——含旧版本 tag
docker image prune -a
# 第四级:清 build cache
docker builder prune
# 组合拳:上面几样一起来(不含 volume)
docker system prune
大多数「磁盘爆满」到二三级就解决了。每条命令都会列出将删除的对象和可回收空间,看一眼再按 y。
需要更大回收空间、且确认没有「只想保留旧 tag」需求时,再用:
docker system prune -a # 含未被容器引用的全部镜像
docker builder prune -af # 强制清 build cache(BuildKit 机器常要这一步)
纯 CI 构建机可以把 -af 写进定时任务;开发本机和生产节点不要默认 -a,否则昨天刚拉的基础镜像今天又要重下。
最危险的开关:--volumes
docker system prune --volumes # ⚠️ 会删除「未被引用」的 volume
数据库容器重建后原 volume 不再被引用?--volumes 会把它连同数据一起删掉。除非你明确知道自己在干什么,否则不要加这个参数。生产环境删 volume 前先备份,或至少:
docker volume ls
docker volume inspect <name> # 看 Mountpoint,确认不是还在用的库
也别直接 rm -rf /var/lib/docker/*——绕过 Docker 元数据,注册表和实际文件对不上,之后所有镜像操作都会出诡异错误。清空间永远用 docker 自己的命令。
三个「清了还会满」的根因
1. 容器日志无上限增长
json-file 日志驱动默认不限大小,一个疯狂打日志的容器能把盘写满:
du -sh /var/lib/docker/containers/*/*-json.log | sort -rh | head -5
彻底解决:/etc/docker/daemon.json 配日志轮转(对新容器生效,旧容器需重建):
{
"log-driver": "json-file",
"log-opts": { "max-size": "50m", "max-file": "3" }
}
改完 sudo systemctl restart docker。单容器也可以在 docker run --log-opt max-size=50m 指定。已经胀破的旧日志,可在确认业务可丢弃后截断:
truncate -s 0 /var/lib/docker/containers/<id>/<id>-json.log
2. 磁盘其实没满,inode 满了
df -h 显示还有空间却仍报 no space?查 inode:
df -i /var/lib/docker
# IUse% 100% ← 小文件(overlay 层、cache)把 inode 耗尽了
解法同样是 prune——层叠文件系统里一个 inode 泄漏源头能造出海量小文件。若 prune 后 inode 仍满,用 du -xh 找异常目录(常见于错误挂载的 hostPath、未清理的临时层)。
3. BuildKit cache 与多架构镜像层
开启 BuildKit 后,docker builder prune 与 docker image prune 清的不是同一堆东西。多架构 manifest(--platform linux/amd64,linux/arm64)会在本机留下多份层;开发机反复 buildx build 却从不 prune cache,几周后「Images 不大、Cache 占了几十 GB」很常见。CI 里每次 job 结束执行 docker builder prune -af 比盲目加盘更划算。
overlay2 与 rootfs:别只盯 Images 一行
docker system df 的 Images 不等于 /var/lib/docker 的全部。可写层(容器内写了大量临时文件)、未清理的 tmp、以及部分存储驱动的内部元数据,都会出现在 du 里但不在 Images RECLAIMABLE 里。排查顺序建议固定为:
df -h+df -i—— 字节还是 inodedocker system df -v—— Docker 自己认账的部分du -xh --max-depth=1 /var/lib/docker—— 对不上账时下钻到overlay2/containers/buildkit- 再决定 prune 哪一级,而不是反过来先 prune 再猜
预防
- CI/CD 构建机加定时任务:每天
docker system prune -af(纯构建机没有 volume 顾虑);BuildKit 机器再加docker builder prune -af; - daemon.json 配好日志轮转(见上),这是最多人漏掉的一条;
- 监控
/var/lib/docker所在分区,告警阈值 80%;同时监控 inode 使用率; - 生产把
/var/lib/docker挂独立分区,爆了不影响系统日志和数据库; - 本机开发:定期清掉不用的旧 tag,而不是等「盘满了再一次性 prune -a」。
写 Dockerfile 时尽量减少无意义的层与巨型 build context(无关文件进 context 会膨胀 cache)。可用本站 Dockerfile 生成器 核对基础镜像与多阶段结构,再用 docker run 转 compose 固化可复现的运行参数。估算镜像体积时,字节单位换算 能避免把 GiB 和 GB 搞混。
排查清单
df -h /var/lib/docker+docker system df— 空间和占比一目了然- 按阶梯执行:container prune → image prune → builder prune
--volumes不到万不得已不加;rm -rf /var/lib/docker永远不用- 清完没多久又满?查容器日志大小
du -sh .../*-json.log df -h有空间仍报错?df -i查 inode- daemon.json 日志轮转配置了吗?BuildKit cache 清了吗?
du与system df对不上时,下钻overlay2/buildkit目录
排查注记
清理命令排成阶梯而不是直接给 system prune -a,是因为文中判断大多数爆满到二三级(清停容器、dangling 镜像)就解决,且每级都会列出将删除的对象——先定位再动手。「三个清了还会满」一节专门收录 df -h 有空间仍报错的 inode 场景,以及 du 和 system df 对不上账的 overlay2 可写层——system df 只算 Docker 自己认账的部分。--volumes 单独拉出一节标成最危险,依据是数据库容器重建后原 volume 不再被引用、会被连数据一起删掉。
本文出自 ToolVault 工具匣。相关工具:Dockerfile 生成器、docker run 转 compose、字节单位换算、Linux 命令速查。相关阅读:Docker daemon 连不上排查、端口被占用 EADDRINUSE、executable file not found。访问 首页 查看更多开发者工具。
相关工具
相关文章
Linux crontab 定时任务实战:命令、日志与排查
crontab 不只是写表达式:三个必会命令、/etc/cron.d 与用户 crontab 的区别、任务没跑的五步排查法、PATH/时区/% 转义、MAILTO,以及秒级任务的正确解法。
docker run 转 docker-compose 完全指南:为什么转、怎么转、参数对照表
把 docker run 命令迁移到 docker-compose.yml 的完整指南:compose 相比裸 run 的五大优势、参数逐项对照表(端口/卷/环境变量/重启策略/网络)、常见坑(裸 -e 透传、匿名卷、--rm 语义),附在线转换器用法。
Permission denied (publickey) 怎么解决?Git 推送失败的 6 种原因与修复
git clone/push 报 Permission denied (publickey) fatal: Could not read from remote repository?覆盖公钥没生成、没加载进 agent、没添加到 GitHub/Gitee、多账号配错密钥、deploy key 权限、remote URL 写错六种根因,附 ssh -v 诊断方法。