
一台跑了半年 Docker 的服务器,磁盘从 40% 涨到 90% 却找不到大文件,是很多运维都遇到过的场景。罪魁祸首通常不是业务数据,而是 Docker 自己攒下的家底:被新版本替代的旧镜像、停止已久的容器、没人引用的数据卷和一层层构建缓存。这篇文章帮助你解决这个问题:先用 docker system df 搞清楚空间到底被谁占掉,再用实测数据对比不同 prune 命令能回收多少空间,最后给出一套既能放心执行、又不误删业务数据的清理顺序。
先看账本:docker system df 摸清占用结构
清理前必须先弄清楚空间去哪了,直接上手删是最容易出事故的做法。docker system df 命令会按镜像、容器、卷、构建缓存四类给出占用总量,相当于 Docker 的磁盘账本:
docker system df
在我们的实测服务器(Ubuntu 22.04,Docker 26.x,运行 14 个容器)上,一次典型输出是:镜像 18.2GB(其中可回收 9.4GB)、容器 1.1GB(可回收 620MB)、卷 22.6GB(可回收 0B)、缓存 3.8GB(可回收 3.8GB)。四个数字说明的问题各不相同:镜像可回收比例高,说明存在大量旧版本镜像没删;卷可回收为零是好事,说明每个卷都还在被使用;构建缓存全部可回收,说明清理它没有任何风险。
想看更细的明细,加 -v 参数可以列出每个镜像、每个卷的具体大小和引用状态。这一步的重点是找出“可回收但不该回收”的部分——比如挂载着数据库文件的卷,虽然可能很大,但绝对不能碰。磁盘占用和性能问题的整体排查思路,也可以参考 服务器相关文章,把 Docker 占用和系统日志、业务文件分开看。

悬空镜像是增长最快的部分
镜像是长期运行的服务器上增长最快的部分。每次拉取新版本镜像,旧镜像不会自动删除,而是变成 <none>:<none> 的悬空镜像(dangling image)留在本地。它们不占运行内存,但实打实占着磁盘。
不带参数的 docker image prune 只删除悬空镜像,安全性最高:
docker image prune
实测这一条命令在我们服务器上回收了 4.1GB,全部是已被新版本替代的旧镜像,对运行中的服务零影响。如果加了 -a 参数,范围会扩大到“所有没有被任何容器使用的镜像”,包括还有用的备用镜像:
docker image prune -a --filter "until=168h"
这条命令实测再回收 5.3GB,但注意 until=168h 过滤器把范围限制在创建超过 168 小时(7 天)的镜像,避免误删最近刚拉的镜像。两个级别的选择标准很简单:日常清理只用第一条;磁盘紧张需要腾空间时,先用第二条的过滤版本,并且执行前用 docker images 确认没有正在灰度发布或随时要回滚的镜像。
停止的容器和构建缓存同样值得回收
停止状态的容器(exited)默认不会再被重启,除非你显式 docker start 它。它们占用的可写层通常不大(实测 620MB),但数量多了也会积少成多:
docker container prune --filter "until=72h"
实测回收 380MB。until=72h 保证只清理停止超过 3 天的容器,给“临时停掉排查问题”的容器留了缓冲期。执行前建议先跑 docker ps -a --filter "status=exited" 扫一眼列表,确认没有需要保留现场的训练或调试容器。
构建缓存是另一个容易忽视的大头。只要服务器上有 docker build 操作,缓存就会持续累积,实测占 3.8GB。它完全不影响运行中的服务,可以放心清理:
docker builder prune
执行后实测回收 3.8GB,代价只是下次构建同一镜像时需要重新拉取基础层和执行构建步骤,时间会变长一些。对于 CI/CD 机器上不常构建的缓存,这笔交换通常很划算。如果你的服务用 compose 编排了多个构建环节,可以参考这篇 Docker Compose 多服务编排教程,把构建和运行环境分开管理。
卷里装着不可恢复的数据
卷(volume)是四类占用中唯一可能装着不可恢复业务数据的部分。数据库文件、上传附件、配置持久化,通常都在卷里。docker volume prune 只删除“没有任何容器引用”的卷,听起来安全,但有一个真实的坑:如果某个容器的数据卷是用 docker compose down(不带 -v)之外的方式停用,或者容器被删了但业务马上要在新容器上重新挂载,这个卷在被 prune 的瞬间就永久丢失了。
我们的实测流程是:先跑 docker volume ls -q -f "dangling=true" 列出所有未被引用的卷,逐个用 docker volume inspect 查看创建时间和挂载点命名,确认既不是数据库卷也不是最近一周内创建的卷之后,才执行清理:
docker volume ls -q -f "dangling=true" docker volume inspect 卷名 # 确认无引用且非业务数据后再执行 docker volume prune --filter "until=168h"
实测我们只回收了 240MB——因为大部分悬空卷其实是测试残留。这个结果反而说明了一件事:卷清理的收益通常不高,但风险等级最高,永远把它放在清理顺序的最后一步,并且宁慢勿快。生产数据库所在的服务器,更建议只做镜像和缓存两级清理,卷留给人工核对。
一键清理命令在生产环境的边界
如果不想分步执行,docker system prune 会一次性清理悬空镜像、停止容器、未使用网络和构建缓存;加上 --volumes 后连悬空卷一起删。它适合两类场景:自己控制的开发机、构建完就可以随时重置的 CI 节点。实测开发机上执行完整版回收了 9.2GB,几乎等于四步清理的总和。
docker system prune --volumes --filter "until=168h"
但在生产服务器上,我们的建议是不要用一键版本:它的过滤粒度是统一的 until,无法对镜像、容器、卷设置不同的保留期;而且 --volumes 一旦带上就没有二次确认的机会。分步执行每一步都能先看清单再删除,出问题的概率低得多。如果磁盘问题反复出现,比起每次手动 prune,更推荐把镜像悬空清理做成每周定时任务,同时检查是不是有容器在疯狂写日志——日志问题可以参考 Docker 日志磁盘爆满排障指南,那里覆盖了 json-file 日志的轮转配置。

实测总结与清理顺序建议
把四步清理的实测数据汇总:悬空镜像 4.1GB、未使用镜像(带过滤)5.3GB、停止容器 380MB、构建缓存 3.8GB、悬空卷 240MB,合计约 13.8GB,服务器磁盘占用从 89% 降到 61%。整个操作在线上业务无感知的情况下完成,验证了“先看账本、再按风险从低到高分步清理”这条路是可行的。
你可以直接按这个顺序操作:先跑 docker system df 摸底;然后 docker image prune 清悬空镜像(零风险);接着 docker builder prune 清构建缓存(零风险);之后带 until 过滤清停止容器和未使用镜像(低风险);最后人工核对后清悬空卷(谨慎)。如果你在为一台长期运行的[独立服务器](https://cn.hostease.com/dedicated-server/)做例行维护,建议把这套清理纳入月度巡检,也可以了解 Hostease 的 独立服务器 方案,把磁盘巡检交给技术支持协助,减少自己半夜处理磁盘告警的次数。