SSD Nodes Learn 8GB 内存 — 每年 $66
指南 Matt Connor作者: Matt Connor · 更新于 2026-08-01

生产服务器 Docker Compose 常用命令速查表

按任务整理 Docker Compose V2 日常命令:启动、应用更改、查看日志、进入 Shell、管理网络与卷,并说明 down、stop 等安全清理的实际区别。

实际会用到的 Compose 命令

Docker Compose 发布了 40 多个子命令。服务器上的日常工作通常只用到其中约 12 个。本页按工作任务对这些命令进行分组,为每个命令说明一个直接原因,并在命令存在易错点时链接到详细说明。

本文中的所有内容都使用 Compose V2:docker compose 中间有一个空格,而不是旧版 docker-compose 脚本。V2 是随 Docker Engine 安装的 Go 插件。V1 已从当前软件包中移除,因此截至 2026 年 7 月,在全新的 Ubuntu 主机上执行 docker-compose: command not found 没有输出是预期行为,并不表示安装损坏。使用 docker compose version 检查。如果该命令没有输出,请安装 docker-compose-plugin 软件包。

以下每个命令都必须从包含 compose.yaml 的目录运行,因为 Compose 从该目录获取项目名称,并以该目录为基准查找文件。在上一级目录运行相同命令时,Compose 会因 no configuration file provided: not found 而停止。如果您还不熟悉文件格式,请先阅读VPS 上的第一个 Compose 文件,然后回到本页查看这些命令。

生命周期:您输入的 4 个命令,以及会删除容器的 1 个命令

docker compose up -d
docker compose up -d --wait
docker compose stop
docker compose start
docker compose restart web
docker compose down

up -d 创建网络、创建容器、启动容器,然后返回。容器一经创建,它就会返回。因此,部署脚本在其后立即执行 curl 探测时,第一次经常会失败。up -d --wait 会阻塞,直到声明了 healthcheck 的所有服务都报告 healthy;如果某个服务始终无法达到该状态,它会以非零状态退出。该标志的可靠性取决于其背后的检查逻辑。因此,在自动化流程中使用它之前,请先编写 Compose 可以信任的 healthcheck

stop 会停止容器但保留容器,因此 start 会使用相同的可写层重新启动相同的容器。down 会停止容器,然后删除容器和项目网络。写入容器内部且位于 volume 之外的任何内容都会随容器一同删除。这是 Compose 中代价最高的误解,down 和 stop 的完整区别介绍了它会在哪些情况下造成影响。

restart 不是重新加载。它会使用容器已有的配置停止并重新启动同一个容器,因此更改的环境变量、新的 image tag 或修改后的端口映射完全不会生效。要应用文件更改,您需要再次运行 up -d。Compose 会将每个服务与其正在运行的容器进行比较,只重新创建配置发生变化的容器。

应用更改:重新创建、拉取或重建

docker compose up -d --force-recreate
docker compose pull && docker compose up -d
docker compose build --no-cache web
docker compose up -d --build web

如果没有任何更改,单独运行 up -d 不会执行任何操作,因此可以安全地重复运行。--force-recreate 会跳过比较,即使配置完全相同,也会替换每个容器。因此,这是清除容器内部异常状态最快的方法。

更新镜像需要运行两条命令,因为它们执行的操作不同。pull 会为文件中指定的每个标签下载最新镜像。随后,up -d 会发现服务的镜像 ID 与正在运行的容器不匹配,并重新创建该容器。跳过拉取步骤时,up -d 会继续运行上个月的 latest,且不会报错。

build 适用于声明 build: 部分而不是 image: 的服务。up -d --build 会在一个步骤中完成构建并启动,这是修改代码时的常规循环。只有在明确某个缓存层已过时时,才使用 --no-cache,因为它会从头重建每一层。

查看正在运行的内容

docker compose ps
docker compose ps -a
docker compose logs -f --tail=100
docker compose logs --since 15m --timestamps db
docker compose top
docker compose ls

ps 只列出正在运行的容器。启动期间崩溃的服务不会显示在其中,除非添加 -a。因此,当 ps 中缺少某个容器,而 ps -a 将其显示为 Exited (1) 时,这通常表示启动失败。先读取退出代码,再读取日志。

logs -f 同时跟踪每个服务,并在每行前添加服务名称。服务之间相互通信且事件顺序很重要时,应使用此视图。指定服务名称可缩小范围。对于已运行一个月的容器,--tail=100 很重要,因为默认设置会输出完整历史记录并占满终端。--since 15m 可以回答您通常关心的问题:刚才执行的重启期间发生了什么。

top 列出每个容器中的进程,从而区分“容器正在运行”和“容器内的进程正在运行”。ls 会离开当前目录,列出主机上的所有 Compose 项目及其状态,便于找到您三个月前启动的堆栈。

在服务中获取 shell

docker compose exec web sh
docker compose exec -u root web sh
docker compose run --rm web env
docker compose run --rm --no-deps web sh

exec 在已运行的容器中执行命令。run 根据相同的服务定义启动一个新容器;当服务运行时间不足以便执行命令时,需要使用它。始终将 run--rm 配合使用,否则每次调用都会留下一个已停止的容器,这些容器会不断累积,直到 docker compose ps -a 无法读取。

先尝试 sh,再尝试 bash。基于 Alpine 的镜像不包含 bash,失败信息为 exec: "bash": executable file not found in $PATH。将 --no-deps 添加到 run 可跳过服务依赖项,避免快速配置检查启动整个数据库。

run --rm web env 是查看服务实际获得的环境的最快方法,此时所有 .env 文件、environment: 块和 shell 变量都已合并。如果某个值不正确,通常是合并顺序导致的;Compose 如何解析 env 文件和 secret 说明了哪个来源具有优先权。

网络、端口和名称解析

docker compose port web 80
docker compose exec web getent hosts db
docker compose config --networks

Compose 会将所有服务放在同一个项目网络中,每个服务名称都是该网络中的 DNS 名称。在 web 内运行 getent hosts db 时,如果名称解析成功,它会输出容器 IP;如果解析失败,则不输出任何内容。因此,您可以在 2 秒内确认“这些容器能否相互访问”。如果名称可以解析,但连接被拒绝,说明 db 内的进程绑定到 127.0.0.1,而不是 0.0.0.0,因此不会接受来自其他容器的数据包。有关这一模型的其余内容,请参阅 Compose 网络和服务 DNS 的工作原理

port web 80 会输出容器端口发布到的主机地址和端口。当映射来自变量时,这可以避免猜测。发布端口还会写入由 Docker 自行管理的防火墙规则。该规则的优先级高于您配置的规则,因此您认为是私有的服务可能会暴露到互联网。有关此情况,请参阅 Docker 发布的端口为何会绕过 ufw

卷和数据

docker compose config --volumes
docker compose cp db:/etc/postgresql/pg_hba.conf ./pg_hba.conf
docker compose down -v

config --volumes 按行输出项目声明的命名卷。你需要备份的就是这份列表。cp 可以在不打开 shell 的情况下,将文件复制到容器中或从容器复制出来。容器一侧使用 service:path 形式。

down -v 会连同容器一起删除这些命名卷。该命令适合拆除测试堆栈,但不适合处理包含重要数据的内容,因为它不会要求确认,也无法撤销。绑定挂载不会受影响,因为它们位于主机文件系统中。由于影响范围不同,你需要谨慎选择绑定挂载和命名卷

清理磁盘空间且不丢失数据

docker compose down --remove-orphans
docker system df
docker image prune -a
docker builder prune

--remove-orphans 会删除属于该项目、但不再出现在文件中的容器。重命名服务后通常会产生这类容器。不使用该选项时,这些容器会继续运行,并且在 docker compose ps 中不可见。

docker system df 会在删除任何内容前显示磁盘空间的使用情况,并分别列出镜像、容器、本地卷和构建缓存,以及每项可回收的空间。image prune -a 会删除没有任何标签指向的镜像。对于已拉取某个大型镜像多个版本的服务器,这通常能释放最多空间。builder prune 会清理构建缓存。在自行构建镜像的服务器上,构建缓存会持续增长且不易察觉。

这些操作都不会处理命名卷。只有 docker volume prunedocker compose down -v 会处理命名卷。

在文件导致问题前检查它

docker compose config --quiet
docker compose config --services
docker compose --dry-run up -d

config --quiet 在验证成功时不输出任何内容,因此应将其放在部署前步骤或 git 钩子中。直接运行 config 会输出完全合并并插值后的文件。通过该输出可以确认变量是否已解析,以及覆盖文件是否按预期叠加。未设置的变量会显示为空值,并伴随警告 The "X" variable is not set. Defaulting to a blank string.

--dry-run 是全局选项,而不是子命令选项,因此应放在 up 之前。它会输出 Compose 将执行的每项操作,但不会进行任何更改。在重要的堆栈上执行 down 前,花费这三十秒进行检查是值得的。

跨文件、配置文件和项目操作

docker compose -f compose.yaml -f compose.prod.yaml up -d
docker compose --profile debug up -d
docker compose -p staging up -d

多个 -f 标志按顺序合并,后面的文件会按键覆盖前面的文件。这是保留一个基础文件并使用小型生产环境覆盖文件的标准方法。不过,列表和映射的规则不同,因此在排查意外结果前,请阅读Compose 如何合并多个文件

--profile 会启动带有该配置文件的服务以及未标记配置文件的服务,从而使调试工具不会出现在常规的 up 中。-p 会设置项目名称,因此同一堆栈的两个副本可以并行运行,并使用独立的网络和卷名称。重启后恢复堆栈不需要手动输入命令,而是由一个单元自动运行命令完成,详见启动时运行 Compose 堆栈

FAQ

什么替代了带连字符的 docker-compose

Compose V2 通过带空格的 docker compose 调用。它是 Docker Engine 附带的插件,当前软件包不再安装 V1 Python 工具。如果空格形式没有输出,请为您的发行版安装 docker-compose-plugin 软件包。请将旧脚本更新为带空格的形式,而不是添加别名,因为 V2 支持 V1 没有的选项。

为什么 docker compose restart 没有应用我的配置更改?

restart 会使用容器创建时的配置停止并启动现有容器,但不会重新读取 compose.yaml。对环境变量、端口、卷或镜像标签的任何更改都需要使用 docker compose up -d。该命令会将每个服务与其运行中的容器进行比较,并重建配置不同的服务。如果希望即使文件没有更改也执行替换,请添加 --force-recreate

如何将服务更新为较新的镜像?

运行 docker compose pull,然后运行 docker compose up -d。pull 操作会获取文件中每个标签对应的当前镜像,up -d 会重建镜像 ID 与容器不再匹配的服务。单独运行 up -d 会复用磁盘上已有的镜像。因此,固定为 latest 的堆栈可能会继续使用数月前的构建,且不会输出错误。

哪些清理命令可安全地在运行中的服务器上执行?

docker system dfdocker image prune -adocker builder prune 只会删除镜像和缓存,因此运行中的服务仍可正常工作,命名卷也不会受到影响。危险的命令组合是 docker compose down -vdocker volume prune,它们会直接删除命名卷而不提示。请先运行 docker compose config --volumes,以确认哪些内容存在风险。

可以只运行一个命令而不启动整个堆栈吗?

可以。docker compose run --rm --no-deps web sh 会根据 web 服务定义启动单个容器,跳过其依赖项,并在退出时删除该容器。如果容器已经在运行,请改用 exec,因为 exec 会加入正在运行的进程,并显示服务的实际状态。