卷:容器数据实际存放在哪里
命名卷与绑定挂载有何区别,数据在磁盘上实际位于何处,以及重建容器时如何避免丢失数据库。
更新于 2026年8月23日
容器是一次性的。写入其中的所有内容都会随容器一起消失。数据必须存放在容器之外,而方式只有两种:命名卷和宿主机目录绑定挂载。
两种方式及各自的适用场景
services:
db:
image: postgres:17
volumes:
- db-data:/var/lib/postgresql/data # named volume
app:
image: nginx:1.27
volumes:
- ./site:/usr/share/nginx/html:ro # directory bind mount
- ./nginx.conf:/etc/nginx/nginx.conf:ro
volumes:
db-data:
命名卷由 Docker 自身创建和存储,位于 /var/lib/docker/volumes/<name>/_data。其中的权限由镜像自动设置。数据库和内部服务状态就是这样保存的。
目录绑定挂载是机器磁盘上的普通路径。您可以用它提供配置文件、网站静态资源和证书:这些文件便于在编辑器中修改,也便于存入代码仓库。
:ro 标志会将挂载设为只读。凡是容器不需要写入权限的地方都应使用它——这是一种成本很低的保护。
所有内容的实际存储位置
docker volume ls
docker volume inspect db-data
docker inspect -f '{{json .Mounts}}' <container> | python3 -m json.tool
sudo du -sh /var/lib/docker/volumes/* | sort -h | tail
最后一条命令是磁盘空间不足时首先要运行的命令。更多细节:磁盘空间。
docker compose down -v和docker volume prune会永久删除卷。带有数据库的卷在容器停止时会显得“未被使用”,从而落入清理范围。清理之前,请先查看列表:docker volume ls -f dangling=true。
复制与迁移
用一次性容器复制命名卷:
docker run --rm -v db-data:/from -v $(pwd):/to alpine \
tar czf /to/db-data.tar.gz -C /from .
恢复:
docker volume create db-data
docker run --rm -v db-data:/to -v $(pwd):/from alpine \
tar xzf /from/db-data.tar.gz -C /to
对于数据库,这种方法只适用于容器已停止的情况:对运行中的数据库进行文件级复制可能得到不一致的结果。最好使用数据库自带的工具进行转储——pg_dump、mysqldump。参见具体要复制什么。
权限
绑定挂载最常见的错误是所有者不匹配。在容器内部,进程以自己的 UID 运行,而宿主机以 root 身份创建的文件对它来说无法访问。
docker compose exec app id
sudo chown -R 1000:1000 ./data
许多镜像接受 PUID/PGID 变量——这比更改目录所有者更方便。
常见错误
- 数据被写入了容器内部。 您在更新时重新创建了容器——数据随之消失。检查方法:
docker diff <container>会显示其文件系统中的变化;其中出现大量写入就是一个警示信号。 - 您把目录绑定挂载到了镜像中非空的路径上。 镜像中的内容被隐藏,服务找不到自己的文件。
- 绑定挂载中使用了相对路径。 它是相对于包含
compose.yaml的目录解析的,而不是当前目录。从其他位置运行会创建一个新的空目录。 - 重装系统时忘记了卷。 重装会完全清空磁盘。副本必须存放在不在这台机器上的地方。