Docker Compose 真实服务器常用命令速查表
按生命周期、变更、日志、Shell、网络、卷和清理整理 Compose V2 命令,说明 compose.yaml 目录要求、down 与 stop 的区别及安全清理方法。
实际会用到的 Compose 命令
Docker Compose 提供数十个子命令。服务器上的日常工作会用到其中约十二个。本页按任务对这些命令进行归类,为每个命令说明一个直接原因,并在命令存在易错点时指向深入介绍。
本文的所有内容都使用 Compose V2 或 V5:docker compose 中间有一个空格,而不是旧版的 docker-compose 脚本。两者都是 Go 插件;在 Linux 上,Compose 插件需要与 Docker Engine 分开安装。Ubuntu 24.04 LTS 仍在其 universe 软件库中提供旧版 Python V1 工具,命令为 docker-compose,版本为 1.29.2-6ubuntu1。使用 docker compose version 检查。如果该命令不可用,请配置 Docker 软件库并安装 docker-compose-plugin 软件包。
以下每条命令都应从包含 compose.yaml 的目录运行;除非显式覆盖,Compose 会将该目录名称用作默认项目名称。如果未指定 -f 或 COMPOSE_FILE,Compose 会在当前工作目录及其父目录中搜索配置文件,因此从上一级目录运行时,只有配置文件仍位于该搜索路径中,才能找到您的堆栈。如果您不熟悉该文件格式,请先阅读VPS 上的第一个 Compose 文件,再返回此处查看这些命令。
生命周期:您会输入的 4 个命令,以及会删除容器的命令
docker compose up -d
docker compose up -d --wait
docker compose stop
docker compose start
docker compose restart web
docker compose downup -d 创建网络和容器,启动容器后返回。它会在容器启动后立即返回,不会等待应用就绪。因此,部署脚本在其后立即执行 curl 探测时,首次尝试通常会失败。up -d --wait 会等待带健康检查的服务变为健康,并等待不带健康检查的服务进入运行状态;如果某个服务不健康,或 --wait-timeout 超时,则会以非零退出码退出。该选项的可靠性取决于背后的检查逻辑。因此,在自动化流程中使用它之前,请先编写 Compose 可以信任的健康检查。
stop 会停止容器但保留容器,因此 start 可以使用相同的可写层重新启动相同的容器。down 会停止容器,然后删除容器和项目网络。写入容器内部且未写入卷的数据都会随容器一起删除。这是 Compose 中代价最高的误解;down 和 stop 的完整区别介绍了它会在哪些情况下造成影响。
restart 不是重新加载。它会使用容器现有的配置停止并重新启动同一个容器,因此修改环境变量、使用新的镜像标签或编辑端口映射都不会生效。要应用文件变更,您需要再次运行 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 webup -d 会在容器配置和镜像均未变化时复用正在运行的容器;镜像拉取和构建行为遵循服务的拉取策略。--force-recreate 会跳过这项比较,即使配置完全相同,也会替换每个容器。因此,这是清除容器内部异常状态最快的方法。
显式更新镜像的流程需要使用两个命令,因为它们执行的操作不同。pull 会为文件中指定的每个标签下载当前镜像。随后,up -d 会发现服务的镜像 ID 已不再匹配正在运行的容器,并重新创建该容器。在默认的 missing 拉取策略下,缓存的标签会被复用,但 latest 除外;该标签始终会被拉取。相反,在包含多个服务的堆栈中,如果同时为每个服务拉取 latest,刚刚还能正常工作的应用可能会立即中断。因此,自托管 AFFiNE 工作区建议将应用镜像和迁移镜像固定到同一个摘要。固定摘要还会将升级变成一次有意的标签修改,之后执行相同的拉取和重新创建操作。对于启动时会迁移数据库的堆栈,应在执行任一命令前准备好数据库转储;自托管 Chatwoot 支持服务台在每次版本升级时都遵循这一流程。
build 适用于声明 build: 部分的服务,包括同时声明 image: 的服务。up -d --build 会在一个步骤中完成构建并启动,这是修改代码时的常规循环。只有在明确确认缓存层已过时时,才使用 --no-cache,因为它会从头重新构建每一层。有些项目会从同一个克隆仓库提供这两种路径,此时两个循环就是可选的更新路径:自托管 openGym 健身追踪器使用 docker compose up -d --build 构建固定的代码版本,因为拉取其 latest 镜像可能会运行比该代码版本更新的发布版本。
查看正在运行的内容
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 lsps仅列出正在运行的容器。启动期间崩溃的服务不会显示在这里,除非添加 -a。因此,容器未出现在 ps 中,而 ps -a 将其显示为 Exited (1),这是启动失败的常见表现。先读取退出码,再读取日志。
logs -f同时跟踪所有服务,并在每行前添加服务名称。当服务彼此通信且事件顺序很重要时,应使用此视图。指定服务名称可以缩小范围。对于已运行一个月的容器,--tail=100 很重要,因为默认行为会输出完整历史记录,使终端充满日志。--since 15m 可以回答通常需要确认的问题:刚才执行的重启期间发生了什么。
top列出每个容器中的进程,从而区分“容器正在运行”和“容器内的进程正在运行”。ls 会跳出当前目录,列出主机上正在运行的 Compose 项目及其状态;添加 -a 可包含已停止的项目。
进入服务内部的 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 shexec 在已运行的容器中执行命令。run 根据相同的服务定义启动新容器。当服务无法持续运行足够长时间,因而无法使用 exec 进入时,需要使用此命令。始终将 run 与 --rm 搭配使用。否则,每次调用都会留下一个已停止的容器,这些容器会不断累积,直到 docker compose ps -a 无法读取。
先尝试 sh,再使用 bash。某些基于 Alpine 的镜像包含 bash,官方 PostgreSQL Alpine 镜像也包含该命令;如果缺少该命令,错误信息为 exec: "bash": executable file not found in $PATH。将 --no-deps 添加到 run 可跳过服务的依赖项,避免快速配置检查启动整个数据库。
run --rm web env 是查看服务实际获得的环境的最快方式,因为此时所有 .env 文件、environment: 块和 shell 变量都已合并。值不正确时,原因通常是合并顺序。Compose 如何解析环境文件和密钥 说明了哪个来源具有优先级。
网络、端口和名称解析
docker compose port web 80
docker compose exec web getent hosts db
docker compose config --networks默认情况下,Compose 会将所有服务放在同一个项目网络中,每个服务名都是该网络上的 DNS 名称。在 web 中运行 getent hosts db(前提是镜像包含 getent)可以检查名称解析,但不能测试 db 是否接受连接。如果名称可以解析,但连接被拒绝,请检查服务器是否正在运行,以及是否正在请求的地址和端口上监听;仅绑定到 127.0.0.1 可能导致此问题。同样的网络边界也说明了:在项目外启动的容器,无论通过 docker run 启动,还是作为独立堆栈运行,都需要共享网络才能解析 jellyfin 这类服务名。当 用于 Jellyfin 媒体库的 Halcyon 前端无法连接到其指向的服务器时,首先应检查这一点。该模型的其余内容见Compose 网络和服务 DNS 的工作方式。
port web 80 会输出容器端口发布到的主机地址和端口。映射来自变量时,这可以避免猜测。发布端口还会写入由 Docker 自行管理的防火墙规则,而且该规则优先于您配置的规则。因此,您以为仅供内部使用的服务可能实际暴露在互联网中。为什么已发布的 Docker 端口会绕过 ufw介绍了这种情况。更安全的做法是保持这些端口不发布,并在项目网络中将一个负责身份验证的代理置于各服务前面。将 Authentik 作为单点登录层运行实现的就是这种架构。
卷和数据
docker compose config --volumes
docker compose cp db:/etc/postgresql/pg_hba.conf ./pg_hba.conf
docker compose down -vconfig --volumes 按行输出项目声明的命名卷。该列表只是备份清单的一部分:还需要检查绑定挂载和配置文件。如果卷中保存着无法替代的数据,确切的备份命令与卷列表同样重要。因此,PhotoPrism 与 Immich 的比较列出了每个照片服务器所需的转储和复制命令。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 默认会删除悬空的构建缓存;添加 -a 可删除所有未使用的构建缓存。
这些命令都不会处理命名卷。docker volume prune --all、docker compose down -v 和 docker volume rm 会删除命名卷。单独使用 docker volume prune 时,只会删除匿名卷。
在出现问题前检查文件
docker compose config --quiet
docker compose config --services
docker compose --dry-run up -dconfig --quiet 会在验证成功时不输出任何内容,因此适合放在部署前步骤或 git hook 中。普通的 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 设置项目名称,因此同一堆栈的两个副本可以并行运行,并使用不同的网络和卷名称。重启后恢复该堆栈,可以使用 restart policy,或使用一个代为运行 Compose 的单元,具体说明请参阅在启动时启动 Compose 堆栈。
FAQ
docker-compose 为什么改成了带连字符的形式?
Compose V2 和 V5 使用带空格的 docker compose 调用。在 Linux 上,Compose 是单独安装的插件;Ubuntu 24.04 LTS 的 universe 仓库仍提供 V1 Python 工具。如果带空格的形式不可用,请配置 Docker 仓库并安装 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 时会遵循拉取策略:默认的 missing 策略会重复使用缓存镜像,但 latest 始终会拉取镜像。
在运行中的服务器上,哪些清理命令是安全的?
docker system df 只报告磁盘使用情况,不会删除任何内容;docker image prune -a 和 docker builder prune 会删除未使用的镜像和构建缓存,因此运行中的服务可以继续工作,命名卷也不会受到影响。需要特别注意的是 docker compose down -v 和 docker volume prune --all。docker compose down -v 会删除项目声明的非 external 命名卷以及已附加的匿名卷,且不会提示确认;docker volume prune --all 会在一次 y/N 提示后删除未使用的本地卷,其中包括命名卷;使用 -f 时则完全不会提示。请先运行 docker compose config --volumes,确认可能受影响的内容。
可以只运行一个命令而不启动整个堆栈吗?
可以。docker compose run --rm --no-deps web sh 会根据 web 服务定义启动单个容器,跳过其依赖项,并在退出时删除该容器。如果容器已经在运行,请改用 exec,因为 exec 会在正在运行的容器中执行命令,并显示服务的实际状态。