Docker Compose stop 和 down 有什么区别?
docker compose stop 仅停止并保留容器,down 还会删除容器和项目网络;两者都不删除命名卷,只有 down --volumes 会删除数据库数据。
简短答案
docker compose stop 会停止容器,但将其保留在磁盘上。docker compose down 会停止容器,然后删除容器以及 Compose 为项目创建的网络。这两个命令都不会操作命名卷。只有添加 -v 时,数据库才会被删除,例如使用 docker compose down -v;该选项会删除 Compose 文件中 volumes 部分声明的命名卷。
以上就是两者的全部区别。本文的其余部分将通过一个 Postgres 卷进行验证:观察它在执行 down 后仍然存在,并在执行 down -v 后消失;同时解释需要使用 --force-recreate 的两种情况。
docker compose stop:容器仍然存在
stop 会向每个容器中的主进程发送 SIGTERM,等待一段时间;如果进程仍在运行,则发送 SIGKILL。默认等待时间为 10 秒,-t 可以修改该时间。此操作不会删除任何内容。容器会保留其 ID、可写层、IP 预留和日志。
docker compose stop
docker compose ps -adocker compose ps 单独使用时只显示正在运行的容器,因此 stop 执行后会输出空表格,让人误以为容器已被删除。ps -a 会包含已停止的容器,此时可以在每个服务旁看到 Exited (0)。使用 docker compose start 即可恢复它们,该命令会重新使用完全相同的容器。
由于容器仍然存在,写入容器内且不在卷中的内容也会保留。这包括使用 docker compose exec 手动安装的软件包,以及在容器内编辑的配置文件。这也是调试时应优先使用 stop 的实际原因:您可以在相同状态下重新启动容器。
docker compose down:删除容器和网络
down 会停止容器,然后删除这些容器以及 Compose 为项目创建的默认网络。Docker 文档将其描述为停止容器,并删除由 up 创建的容器、网络、卷和镜像。但只有在使用 -v 和 --rmi 请求时,才会删除卷和镜像。
docker compose down
docker compose ps -a
docker network ls执行 down 后,ps -a 不会为该项目输出任何内容,<project>_default 网络也会被删除。除非在 Compose 文件中设置 name:,或传入 -p,否则项目名称取自目录名称。容器可写层中的所有更改现在都无法恢复。因此,应将 down 视为会丢弃容器、但保留卷中数据的命令。
如果在错误的目录中运行该命令,则会得到 no configuration file provided: not found。Compose 不知道您指的是哪个项目,因此会拒绝执行。当前不在项目目录中时,请使用 docker compose -f /srv/myapp/compose.yaml down。
docker compose down 会删除我的卷吗?
不会。在顶层 volumes 键下声明的命名卷会独立于 down,也独立于其挂载的容器而存在。这是用户对该命令最常见的担忧。在 Compose v2 中,答案始终如此。
准备一个可用于测试的堆栈。在名为 voltest 的空目录中,将以下内容写入 compose.yaml。
services:
db:
image: postgres:16.4
restart: unless-stopped
environment:
POSTGRES_PASSWORD: example
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:启动堆栈,并写入一行稍后可以识别的数据。
docker compose up -d
sleep 10
docker compose exec -T db psql -U postgres -c "create table marker (note text);"
docker compose exec -T db psql -U postgres -c "insert into marker values ('survived');"现在删除容器并检查卷。
docker compose down
docker volume ls输出中仍会列出 voltest_pgdata。容器已删除,但数据仍然存在。重新启动堆栈并读取该行。
docker compose up -d
sleep 10
docker compose exec -T db psql -U postgres -c "select * from marker;"你会得到一行包含 survived 的数据。新容器与原容器不同,其 ID 也不同,但它挂载的是同一个卷。如果需要了解更完整的信息,请参阅Compose 基础指南,其中介绍了命名卷与绑定挂载的区别,以及每种卷在主机上的实际位置。
down -v 确切会删除什么
-v(完整形式为 --volumes)会删除 Compose 文件中 volumes 部分声明的具名卷,以及连接到容器的匿名卷。请对同一个堆栈运行该命令。
docker compose down -v
docker volume lsvoltest_pgdata 不再列出。再次启动堆栈后,Postgres 入口点会发现数据目录为空,因此初始化一个新的集群。容器日志会明确显示这一点。
The files belonging to this database system will be owned by user "postgres".
PostgreSQL init process complete; ready for start up.如果一个已经运行数月的堆栈出现这段日志,说明卷已被删除。您的 marker 表已丢失,恢复的唯一方法是使用备份。
-v 永远不会删除某些存储。绑定挂载使用主机路径,因此 Docker 只会卸载它,文件仍保留在原位置。标记为 external: true 的卷声明属于此项目之外的对象,Compose 永远不会删除它。在运行 down -v 之前从 Compose 文件中删除的具名卷已不再声明,因此 Compose 不知道要删除它,会将其作为孤立卷留在 docker volume prune 中。
最后一种情况常在重构期间造成问题。从文件中删除服务及其卷后再运行 down -v,该卷仍会保留,因为文件已不再提及它。请在编辑文件之前运行 down -v,不要在编辑之后运行。
实际需要使用 --force-recreate 的场景
docker compose up -d 不会每次都重建所有内容。Compose 会将每个服务的已解析配置哈希值作为标签存储在容器上。如果哈希值和镜像 ID 都匹配,容器会保持不变,此时会得到 Container voltest-db-1 Running,而不是 Recreated。这几乎总是您需要的行为,因为这样可以安全地重复运行 up -d。
这也是某些编辑看起来没有效果的原因。Compose 对已解析的服务定义进行哈希计算,而不是对该定义所引用的文件内容进行哈希计算。挂载到容器中并仅在启动时读取一次的配置文件,在您编辑后不会触发容器重建,因为挂载路径没有变化。服务会继续使用启动时读取的值运行。
docker compose up -d --force-recreate该命令会停止并删除每个容器,然后根据相同的定义创建新容器。编辑挂载的配置文件后应使用该命令;当容器状态发生无法解释的偏移时,也应使用该命令。卷不会受到影响,因此数据库数据会在强制重建后保留。若要获取同一标签对应的更新镜像,还需要同时执行拉取操作。
docker compose pull
docker compose up -dpull 会获取新的镜像 ID,随后 up -d 会发现该 ID 与运行中容器的镜像 ID 不同,并自行重建容器。单独添加 --force-recreate 而不添加 pull,只会使用同一个旧镜像创建新容器。因此,“我执行了强制重建,但版本仍然是旧的”是很常见的问题。
docker compose restart 不会执行上述任何操作。它只会重启现有容器,完全不会重新读取 Compose 文件,因此修改后的环境变量或端口映射不会生效。如果您编辑了该文件,请使用 up -d。
需要牢记的思维模型
容器是可替换的。容器由一个进程和一个薄的可写层组成,Compose 大约可以在 1 秒内根据文件构建出完全相同的容器。卷不可替换,因为它们保存着唯一的状态副本,仓库中的任何文件都无法重新生成这些状态。
每个 Compose 命令都对应这种划分。stop 和 start 会保留容器。down 和 up 会替换容器并保留卷。down -v 是唯一会移除状态的常规命令,因此需要显式标志。在任何真实环境中执行该命令前,请确认您已有备份,并且至少成功恢复过一次。
同样的逻辑也适用于机密信息。通过 POSTGRES_PASSWORD 设置的密码只会在数据库首次初始化时读取,因此修改环境文件中的密码并运行 up -d 后,您得到的是 password authentication failed for user "postgres"。容器是新的,但卷是旧的;旧卷仍保存旧密码。Compose 如何解析环境文件和机密信息说明了同一变量被设置两次时,哪一层的设置会生效。
故障模式和您将看到的字符串
no configuration file provided: not found 表示 Compose 在一个既没有 compose.yaml 也没有 docker-compose.yml 的目录中运行。请使用完整路径传递 -f。
network voltest_default has active endpoints 出现在 down 上,表示项目外部的容器已连接到项目网络。通常,这是使用 docker run --network 手动启动的容器。删除该容器,然后再次运行 down。
重命名或删除服务后会出现 Found orphan containers ([voltest-old-1]) for this project。旧容器仍带有项目标签。docker compose down --remove-orphans 会清除这些容器,在运行正常的堆栈上执行此命令是安全的。
手动执行 docker volume rm 时出现 Error response from daemon: remove voltest_pgdata: volume is in use,表示仍有容器引用该卷,其中可能包括已停止的容器。先运行 docker compose down,然后删除该卷;或者直接使用 down -v。对于较大的项目,请参阅多服务 Compose 堆栈,了解单个项目可能累积多少个卷。
FAQ
docker compose down 会删除我的数据库吗?
如果数据库位于命名卷或绑定挂载中,则不会。down 会删除容器和项目网络,但卷仍保留在磁盘上,数据也保持完整。下次运行 docker compose up -d 时,新容器会挂载同一个卷,数据仍然存在。只有 docker compose down -v 会删除命名卷,而且只会删除 Compose 文件 volumes 部分中声明的命名卷。
对于需要稍后恢复的容器,stop 和 down 有什么区别?
stop 会保留容器,因此 docker compose start 会让您回到同一个容器,并保留相同的可写层。您在容器内手动安装或编辑的内容仍然存在。down 会删除容器,因此下次运行 up -d 时,会根据镜像创建一个全新的容器,这些手动修改也会丢失。调试期间,请使用 stop。
如何删除 Compose 项目创建的所有内容?
docker compose down -v --rmi all --remove-orphans 会删除容器、项目网络、文件中声明的命名卷、服务使用的镜像,以及所有仍带有项目名称标签的容器。它不会处理绑定挂载或标记为 external: true 的卷。运行该命令前,先使用 docker volume ls 查看即将删除的内容。
为什么容器没有应用我对挂载配置文件所做的修改?
Compose 会通过比较解析后的服务定义的哈希值来决定是否重新创建容器,但该哈希值不包含挂载文件的内容。路径没有变化,因此 Compose 会让容器继续运行,并继续使用容器启动时读取的值。运行 docker compose up -d --force-recreate,创建一个会重新读取该文件的新容器。
为什么我修改后的新 POSTGRES_PASSWORD 没有生效?
Postgres 镜像只会在初始化空数据目录时读取 POSTGRES_PASSWORD。您的卷中已经存在初始化完成的集群,因此该变量会被忽略,旧密码仍然有效。您会看到 password authentication failed for user "postgres"。在运行中的数据库内使用 ALTER USER 修改密码,或者接受数据丢失,并使用 docker compose down -v 重新开始。