Docker Compose 环境变量改了却没生效?.env、env_file 与容器重建排查

修改 .env 后重启容器,应用仍然使用旧配置,往往需要区分 Compose 插值、容器环境与应用配置。本文核对项目参数及变量覆盖来源,说明 restart 与重新创建容器的区别,提供目标服务更新、持久数据检查、业务验收和回退步骤,帮助管理员避免无效重启和误删数据,也便于部署脚本固定正确的配置来源。
Docker Compose 环境变量改了却没生效?.env、env_file 与容器重建排查

把 .env 里的配置改了,再执行 docker compose restart,应用还是连接旧地址,运行模式也没变。遇到这种情况,要分别检查三层:Compose 解析到了什么配置、现有容器带着什么环境,以及应用实际采用了什么值。

restart 会重新启动已有容器,不会把修改后的 Compose 环境配置重新写进容器。 Docker 官方文档明确说明,修改 Compose 配置后运行 restart,环境变量等变更不会因此生效。[1] 但也不能一看到旧值就直接删除容器:有时改错了文件,有时值被其他来源覆盖,应用还可能优先读取自身配置文件。

本文面向使用 docker compose 命令的 Compose 插件。旧版 docker-compose 的参数支持可能不同,请先运行 docker compose version。命令中的路径、项目名和服务名都是示例,需要替换成真实部署值。本文依据官方文档整理,没有在你的生产环境执行这些操作。

一、先确认操作的是哪个项目

同一台服务器上可能有测试、生产和旧部署目录。手动运行命令、定时任务及 CI 脚本也可能使用不同的 -f、-p、--env-file 参数。

先查看当前项目和版本:

docker compose version
docker compose ls

然后使用部署时一致的参数查询目标服务:

# /srv/example、example 和 web 均为示例
# compose.yaml 与 .env.prod 必须对应这次真实部署
docker compose -p example \
  -f /srv/example/compose.yaml \
  --env-file /srv/example/.env.prod ps

后文沿用同一组参数,避免排查过程中切换到另一套配置。如果原部署用了多个 -f 文件,应按原顺序全部保留;如果指定了项目目录,也应保持一致。别为了套用示例而给已有项目换一个名字。

二、.env 和 env_file 作用不同

.env 常用于给 Compose 文件中的 ${变量名} 提供插值。服务的 env_file: 则用于向容器提供环境变量。两者可以配合使用,但不能仅凭名字判断实际效果。[3][4]

例如,项目目录中的 .env 写着:

APP_MODE=production

如果 Compose 文件没有引用 APP_MODE,也没有通过服务配置把它传给容器,仅修改这个文件并不保证容器会出现该变量。

下面是显式传入的配置示例:

services:
  web:
    image: example/web:stable
    environment:
      APP_MODE: ${APP_MODE:?APP_MODE must be set}

镜像名称只是占位示例,不是可直接部署的推荐镜像。${APP_MODE:?...} 在变量未设置或为空时让解析报错,适合必须明确配置的普通字段。

也可以使用服务环境文件:

services:
  web:
    image: example/web:stable
    env_file:
      - ./app.env

app.env 内容示例:

APP_MODE=production
LOG_LEVEL=info

服务 env_file 的相对路径通常相对于 Compose 文件位置解析。[4] 命令行的 --env-file 是另一种输入:它指定 Compose 插值使用的环境文件。为了减少路径歧义,部署脚本可以明确使用绝对路径。

Compose 发现默认 .env 的规则还受到工作目录、项目目录及相关选项影响。[3] 排查时优先核对实际命令,而不是认定“当前目录这份文件一定被读取了”。

三、检查最终配置,定位覆盖来源

先验证这组文件是否能正常解析:

docker compose -p example \
  -f /srv/example/compose.yaml \
  --env-file /srv/example/.env.prod config --quiet

需要查看具体结果时,使用 config:

docker compose -p example \
  -f /srv/example/compose.yaml \
  --env-file /srv/example/.env.prod config

该命令合并配置文件、处理插值并展示 Compose 将采用的模型。[6] 结果可能包含数据库密码、令牌和连接串;只在受控终端查看,不要把完整输出直接发到工单或公共聊天。

本机版本支持时,还可以用 config --environment 查看插值环境。这个结果同样可能包含敏感值,它也不等于现有容器的运行环境。

覆盖关系可以从两个角度查:

  • 插值来源:当前 shell 中的同名变量可能覆盖环境文件提供的值;默认 .env 或 --env-file 的实际选择也会影响结果。[3]
  • 容器环境来源:同名变量同时出现在服务 environment: 与 env_file: 时,environment: 优先;镜像里的 ENV 默认值也可能被 Compose 配置覆盖。[4][5]

例如,app.env 已改为 LOG_LEVEL=info,但 Compose 文件还保留 environment: LOG_LEVEL=debug,容器就可能继续收到 debug。改文件之前先找出最终值来自哪一层,能少做一次无效重建。

docker compose run -e 能对一次性容器传入变量,其优先级另有规则;不要用它启动的新容器来证明长期运行的 web 已经更新。[5]

四、读取已有容器的值,区分“配置正确”和“容器更新”

假设 APP_MODE 是不含敏感信息的变量,并且镜像中有 printenv,可以查看当前服务容器:

docker compose -p example \
  -f /srv/example/compose.yaml \
  --env-file /srv/example/.env.prod \
  exec -T web printenv APP_MODE

该命令只查询指定变量;不要省略变量名后批量输出全部环境。精简镜像可能没有 printenv,此时按镜像提供的工具或受控诊断方式检查,不能因为命令不存在就判定环境变量没生效。

还要注意,exec 启动的诊断进程不能完整代表应用主进程经过入口脚本、框架初始化后使用的配置。它适合与 config 做初步对照,最终仍要验证应用行为。

这时通常会出现三种结果:

  • 最终 Compose 配置已经是旧值:回到文件、参数和覆盖来源检查。
  • Compose 配置是新值,现有容器仍是旧值:检查容器是否真正重建。
  • 容器查询已是新值,应用行为仍旧:继续检查应用配置优先级、缓存或访问目标。

五、重建之前,确认数据不只放在容器可写层

docker compose up 会在检测到服务配置或镜像变化时停止并重建相关容器,同时保留已挂载卷。[2] 但容器自身可写层里的临时修改不属于持久卷,重建后可能丢失。

操作前需要确认:

  • 数据库、上传文件和业务状态是否放在命名卷、正确的绑定挂载或外部存储中。
  • 有没有手动拷进容器、却没有纳入镜像或持久挂载的文件。
  • 旧的 Compose 配置、环境文件、镜像标签或 digest 是否有可恢复记录。
  • 这次改动是否还涉及数据库结构、密钥轮换或其他无法单靠换回容器恢复的操作。

备份要包含必要的持久数据,并限制敏感配置备份的访问权限。数据库备份应采用产品支持的一致性方案,不能只复制正在写入的数据目录。

如果应用是单实例,重建会中断服务。先安排维护窗口或流量切换,预留启动时间和回退条件。--no-deps 不代表业务毫无影响,也不会自动把整个依赖链验证一遍。

六、只重建目标服务,保持部署参数一致

完成配置与数据检查后,可以让 Compose 应用这次变更:

docker compose -p example \
  -f /srv/example/compose.yaml \
  --env-file /srv/example/.env.prod \
  up -d --no-deps web

如果需要明确要求替换容器,可在维护窗口使用:

docker compose -p example \
  -f /srv/example/compose.yaml \
  --env-file /srv/example/.env.prod \
  up -d --no-deps --force-recreate web

--force-recreate 会在配置或镜像未变化时也重建容器;--no-deps 用于限制本次操作,不主动启动关联服务。[2] 如果依赖本身也改了,就不能只重建 web 后期待整套系统自动同步。

这里不需要先执行 down,也不需要删除卷。尤其不要为了更新一个变量而附带删除持久卷或重新初始化数据库。卷的保留与业务数据可恢复也不是同一回事,前者不能替代备份。

若变量用于 Dockerfile 的 ARG 或前端静态构建,它可能在镜像构建时就被固化。此类修改需要按构建流程重新生成镜像、确认版本,再重建运行容器;仅更新运行环境未必改变已经生成的文件。

七、验收新容器、变量与真实请求

重建完成后,继续使用相同参数检查服务状态与近期日志:

docker compose -p example \
  -f /srv/example/compose.yaml \
  --env-file /srv/example/.env.prod ps

docker compose -p example \
  -f /srv/example/compose.yaml \
  --env-file /srv/example/.env.prod \
  logs --since 10m --tail 100 web

日志可能含敏感信息,同样需要限制查看和分享范围。对比操作前后的容器 ID 或创建时间,再查询那个不敏感的变量,确认本次确实更新了目标容器。

随后用真实业务功能验证。例如修改后端地址时,检查请求是否到达预期后端;修改日志级别时,检查后续日志;涉及数据库时,检查连接与必要的读写功能。

如果还出现旧行为,可以继续查入口脚本是否覆盖配置、应用是否优先读取配置文件、配置缓存是否需要按产品文档刷新,以及负载均衡是否把请求送到其他旧实例。不要在日志中打印完整数据库 URL 或密钥来“证明新值”。

失败时,恢复此次修改的配置和环境文件,使用已记录的旧镜像版本,按相同项目参数重新创建目标服务,再验收。若已执行数据库迁移或改变外部系统,仅恢复环境变量可能不够,需要按相应产品的回退计划处理。

.env 的修改、容器的创建配置和应用最终行为,是三个需要分别验证的结果。把部署参数、变量来源和持久数据位置记录清楚,后续更新就能按确定的流程完成。

参考资料

资料核验日期:2026 年 10 月 12 日。本文命令为文档核验后的示例,未在读者的生产环境执行。

  1. Docker 官方文档:docker compose restart。https://docs.docker.com/reference/cli/docker/compose/restart/
  2. Docker 官方文档:docker compose up。https://docs.docker.com/reference/cli/docker/compose/up/
  3. Docker 官方文档:Variable interpolation,.env 与 –env-file。https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/
  4. Docker 官方文档:Compose services,environment 与 env_file。https://docs.docker.com/reference/compose-file/services/
  5. Docker 官方文档:Environment variables precedence。https://docs.docker.com/compose/how-tos/environment-variables/envvars-precedence/
  6. Docker 官方文档:docker compose config。https://docs.docker.com/reference/cli/docker/compose/config/
Docker与容器知识库

Docker容器无法访问外网怎么办?DNS解析、默认网关与防火墙排查教程

2026-8-25 14:56:50

知识库

ARP 异常导致内网频繁掉线?企业私有云网络稳定性优化全攻略

2025-7-4 12:06:05