Docker 磁盘空间不足怎么办:镜像、构建缓存、日志与数据卷清理指南

Docker 提示 no space left on device 怎么办?先用 docker system df 定位镜像、容器和构建缓存,再检查日志与挂载目录,分清 prune 和 compose down 的删除范围,设置日志轮转并验证业务数据。

Docker 磁盘满了,先运行 docker system df -v 查看资源占用,再检查容器日志和挂载目录。优先清理确认可重建的构建缓存、悬空镜像与废弃容器;数据卷需要单独确认归属和备份,不能因为显示“未使用”就删除。

自托管服务运行几个月后,镜像版本、构建缓存、日志和业务数据可能一起增长。 一次 prune 只能处理其中一部分,盲目扩大清理范围还可能删掉数据库。

本文以 Linux Docker Engine 和 Compose V2 为主要环境。 Docker CLI 查询命令通常也能在 Windows PowerShell 中执行;标为 Bash 的宿主机命令应在实际运行 Docker 的 Linux 主机执行。 文档核对日期为 2026 年 10 月 22 日,本文没有在你的服务器上执行清理或重建操作。

如果你从 Docker 部署 Syncthing 或 Joplin Server 自托管教程 延伸到这里,先找到那些服务实际使用的数据目录,再决定清理对象。

先确定满的是哪块盘、连接的是哪台主机

Docker CLI 可能连接本机、远端 SSH context,或 Docker Desktop 的 Linux 虚拟机。 在错误的 context 下查看空间,得到的数字可能和当前服务器完全无关。

1
2
3
4
5
docker context show
docker context ls
docker version
docker info
docker compose version

重点核对服务端信息、当前 context 和 Docker 数据目录。 Linux 上默认数据目录经常是 /var/lib/docker,但 rootless、自定义配置和不同存储后端可能使用其他路径。 不要把这个默认路径直接当成待删除目录。

在 Linux 宿主机检查文件系统容量和 inode:

1
2
df -h
df -i

如果磁盘还有容量,但 inode 已经耗尽,大量小文件也会触发 no space left on device。 如果写入目标是绑定挂载的其他磁盘,则应检查那个挂载点,而不是只看系统盘。

Docker Desktop 还存在虚拟磁盘这一层。 容器内部释放的空间、虚拟磁盘的可用容量、宿主机磁盘文件实际占用,可能不会同步变化。 因此,Windows 的 C 盘没有立即变小,不能单独证明 Docker 清理失败。 本文不提供通用于所有 Desktop 后端的虚拟磁盘压缩命令。

用占用明细区分五类数据

先执行只读查询:

1
2
3
4
5
docker system df
docker system df -v
docker ps -a --size
docker image ls
docker volume ls

docker system df 展示 Docker 资源占用,-v 展开镜像、容器和本地卷的明细。 镜像之间可能共享层,不能把每个镜像显示的 SIZE 简单相加当成实际占用。

类型 常见内容 优先处理方式
镜像 当前版本、旧版本、构建中间产物 保留回滚版本,检查后清理悬空镜像
容器可写层 未持久化的上传文件、临时文件 确认数据能否丢弃或迁出
构建缓存 Dockerfile 各步骤的可复用结果 按时间清理,接受后续构建变慢
数据卷与绑定挂载 数据库、配置、附件、模型 按应用管理,先备份再调整
容器日志 标准输出、标准错误 查明日志驱动,配置轮转

RECLAIMABLE 表示符合资源回收条件的占用估算,不能据此判定业务是否还需要这些资源。 比如旧镜像可能是唯一可用的回滚版本,未被引用的数据卷可能存着已经下线但仍需保留的数据库。

docker ps --size 中的可写层大小,也不等于挂载数据和日志的总大小。 如果这里显示很小,而宿主机磁盘仍在快速增长,就要沿着后面两条线检查。

清理前先画清容器与数据的对应关系

假设待检查容器名为 my-app,先替换成自己的实际名称:

1
2
3
docker inspect my-app --format '{{json .Mounts}}'
docker inspect my-app --format '{{json .HostConfig.LogConfig}}'
docker inspect my-app --format '{{.LogPath}}'

在 Mounts 中重点看:

  • Type:区分 volume 和 bind。
  • Name:Docker 管理的数据卷名称。
  • Source:宿主机来源路径。
  • Destination:容器内的挂载位置。
  • RW:容器能否写入。

例如,容器内 /data 映射到宿主机 /srv/my-app/data,那么该目录增长需要在宿主机和应用层排查。 删除旧镜像不会缩小这里的附件库。

如果数据库数据仍写在容器可写层里,先安排备份和持久化迁移。 此时删除容器可能直接丢失数据,不适合进入下面的容器清理步骤。

Compose 项目还应在正确的项目目录运行:

1
2
3
docker compose config --quiet
docker compose ps -a
docker compose images

有自定义 -f、-p 或环境文件时,查询与后续操作要使用同一组参数。 改变项目名可能创建另一组卷,让应用看起来像“数据清空了”。

记录实际卷名、Compose 文件和镜像版本,备份文件放到有足够空间的独立位置。 数据库使用其自身支持的逻辑备份或一致性快照方式;运行中直接打包数据库目录可能得到无法恢复的备份。

镜像和构建缓存:从可重建的内容开始

先评估构建缓存

如果空间主要来自 Build Cache,清理缓存通常比触碰业务卷更合适。 代价是下次构建可能重新下载依赖、编译并消耗更多时间。

1
docker builder prune --filter 'until=168h'

这会按时间条件清理符合条件的构建缓存,并保留交互确认。 168h 是七天,属于示例保留窗口,应根据构建频率调整。 它不是“预览”,确认后会实际删除缓存。 具体参数见 docker builder prune。

使用 Buildx 的多 builder 环境时,还要检查当前 builder:

1
2
docker buildx ls
docker buildx du

docker buildx du 查询当前选中 builder 的缓存占用。 不要在一个 builder 上看到缓存大,却在另一个 builder 上反复执行清理。 多台远端构建机也需要分别核对,不能假定它们共享本机磁盘。

再判断悬空镜像能否删除

列出悬空镜像:

1
docker image ls --filter dangling=true

确认这些构建产物已经不需要后,可以执行:

1
docker image prune

默认清理悬空镜像。 加 -a 后范围扩大到没有容器引用的镜像,包括带有正常标签的旧版本。 这会影响离线恢复和版本回滚,不能把它当成默认加速参数。 完整规则见 docker image prune。

如果只想删除一个已经确认废弃的镜像,指定实际镜像 ID 更容易控制范围:

1
docker image rm IMAGE_ID

遇到引用冲突时,先检查引用它的容器,重新判断是否仍需保留。 不要立即添加强制参数绕过这个提示。

废弃容器按名称逐个处理

停止状态不代表容器里的文件没有价值。 先查看容器的挂载和可写层,再决定是否删除:

1
2
3
docker ps -a --filter status=exited --size
docker inspect old-test-container --format '{{json .Mounts}}'
docker diff old-test-container

docker diff 可用于查看容器文件系统变更,不是备份,也不能代替检查挂载目录。 确认这是可丢弃的测试容器后,才删除指定对象:

1
docker container rm old-test-container

此处没有使用 -f 或 -v。 删除容器仍会丢弃其可写层和容器日志;重要文件必须提前导出。 删除规则可查 docker container rm。

日志持续增长:清缓存之后还要处理根因

如果 docker system df 中可回收空间很少,继续查看日志驱动和路径。 Docker 默认的 json-file 日志驱动没有默认大小限制,高频错误输出可能持续占用磁盘。

1
2
3
4
docker info --format '{{.LoggingDriver}}'
docker inspect my-app --format '{{json .HostConfig.LogConfig}}'
docker inspect my-app --format '{{.LogPath}}'
docker logs --tail 100 my-app

默认驱动和某个容器的实际驱动可能不同,以容器的 LogConfig 为准。 LogPath 也不一定始终提供可直接查看的文件路径,取决于日志驱动和运行环境。 配置行为见 Docker 日志驱动文档。

在 Linux 上,可对实际返回的路径执行只读大小查询。 例如把下面路径替换为刚查询到的日志文件:

1
sudo du -h /actual/path/to/container-json.log

不要直接删除或截断 Docker 管理的日志文件。 官方说明这些文件由 Docker daemon 独占管理,外部修改可能干扰日志系统。 如果日志是不断重复的数据库连接失败或权限错误,先解决应用问题,单纯轮转仍会产生大量无用日志。

为 Compose 服务设置日志轮转

把下面的 logging 配置合并到现有服务中,保留原来的镜像、端口、环境变量和数据挂载:

1
2
3
4
5
6
7
services:
  app:
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

这只是配置片段,不是可独立启动的完整 Compose 文件。 app 要替换成实际服务名。 max-size 控制单个日志文件的轮转阈值,max-file 控制保留文件数量;具体规则见 json-file 文档。

修改后先检查语法,在业务允许短暂中断、数据已持久化并备份的前提下重建目标服务:

1
2
3
docker compose config --quiet
docker compose up -d --no-deps --force-recreate app
docker compose ps

重建会替换容器,其可写层内容不会自动保留。 如果服务依赖匿名卷,应先明确后续如何复用或迁移这些卷,再执行重建。 随后找到新容器的实际名称并检查:

1
2
docker inspect my-app --format '{{json .HostConfig.LogConfig}}'
docker logs --tail 100 my-app

只修改配置后运行 docker compose restart 不会更新容器创建时确定的日志选项。 如果检查仍显示旧配置,核对服务名、Compose 文件、项目名和实际容器 ID。

轮转会限制本地历史日志,需长期审计的业务应另行采集与保留。 应用自己写入挂载目录的日志,也需要应用配置或合适的轮转机制;Docker 的标准输出轮转不会管理那些文件。

数据卷:未使用和可删除是两回事

列出没有容器引用的卷,可以帮助找到待核查对象:

1
2
3
docker volume ls --filter dangling=true
docker volume inspect my-project-data
docker ps -a --filter volume=my-project-data

卷没有关联容器,可能只是因为执行过 docker compose down。 容器删除后,卷里的数据库文件仍可能是下一次启动所需的全部业务数据。 卷的持久化机制见 Docker Volumes 文档。

实际处置前,应能回答四个问题:

  1. 这个卷属于哪个应用和哪个环境?
  2. 应用是否已经永久下线,还是准备稍后重建?
  3. 是否存在独立备份,且验证过恢复方法?
  4. 备份存放位置是否会被这次清理一并删除?

只有确认不再需要的指定卷,才考虑用 docker volume rm 实际卷名 删除。 本文不把批量数据卷清理作为日常维护步骤。 数据卷增长来自数据库记录、附件或模型时,应先使用应用支持的保留策略、归档或迁移流程。

prune 与 compose down 的边界

不同命令的删除范围不能混用:

命令或选项 需要理解的范围
docker image prune 默认针对悬空镜像
docker image prune -a 扩展到所有没有容器引用的镜像
docker system prune 涉及停止容器、未使用网络、悬空镜像和未使用构建缓存
docker system prune --volumes 额外涉及未使用匿名卷,不能理解为备份清理
docker volume prune 当前默认清理未使用匿名本地卷;--all 扩大到未使用命名卷
docker compose down 默认移除项目容器和网络,不主动删除数据卷
docker compose down -v 额外删除声明的命名卷和附属匿名卷;external 卷除外

参数行为以安装版本的 --help 和当前官方文档为准。 参见 system prune、volume prune 和 compose down。

批量清理不是一次可撤销的操作。 缓存可以重新生成,镜像可能重新拉取,已删除的数据卷则需要有效备份才能恢复。 另外,Compose 默认不删除匿名卷,不代表下次 up 一定能自动挂回原来的匿名卷。 长期业务应使用明确的数据路径或命名卷,并保存对应配置。

清理后用空间变化和业务读写共同验收

每处理一类资源就重新查看一次,便于知道究竟释放了哪里:

1
2
3
4
docker system df -v
docker ps -a --size
docker compose ps
docker compose logs --tail 100

Linux 宿主机再检查:

1
2
df -h
df -i

不能只凭容器显示 Up 就宣布恢复。 至少打开一次服务、读取一条已有记录、写入一条可删除的测试记录,再确认数据库或同步目录确实使用原来的数据。 涉及日志重建的服务,还要观察一段时间的磁盘增长,确认轮转配置和应用错误都已处理。

如果释放量明显少于预期,按现象继续定位:

现象 下一步检查
镜像列表很大,实际只释放少量空间 镜像共享层、仍被容器引用的层
Build Cache 没变化 当前 context 和 builder 是否一致
Docker 资源很小,磁盘仍满 绑定挂载目录、日志、其他宿主机文件
Linux 仍报空间不足 实际目标文件系统、inode、配额和存储后端
Desktop 内可用空间增加,宿主机占用不变 对应平台的虚拟磁盘回收机制
容器重建后出现初始化页面 项目名、卷名、绑定路径或匿名卷关联发生变化

最后一种情况应立即停止继续写入,核对旧卷是否还在、配置是否指向新目录。 不要接着运行卷清理,否则可能把仍可找回的旧数据真正删除。

若清理后服务异常,先还原 Compose 配置,检查持久化挂载,再使用保留的镜像版本重建。 如果卷已经删除,重新拉镜像不会恢复业务数据,需要按事先验证过的备份流程恢复。

后续维护怎样安排

维护记录至少包括 Docker context、资源占用、实际数据目录、日志驱动和备份位置。 构建频繁的开发机可以定期评估缓存;长期运行数据库的服务器,应把容量告警、数据库备份和恢复演练放在更高优先级。

不要把带强制参数、镜像全量清理和卷清理的命令直接放进定时任务。 先运行一段时间的只读统计,确认哪些资源稳定增长,再选择对应的保留策略。

需要把数据迁到另一块盘时,可参考 rsync 增量同步与校验。 对数据库目录,仍需先停止相关写入或使用应用支持的一致性备份;文件复制成功不等于数据库备份可恢复。