Docker Compose环境变量为什么没生效
改了 .env,容器却还在使用旧值,通常不是文件没有保存,而是改错了变量来源,或者只重启了旧容器。排查时要分清三步:Compose 读取什么值、服务配置把什么值交给容器、运行中的容器实际拿到了什么。
先分清两种“环境文件”
| 位置 | 作用 |
|---|---|
Shell、.env、--env-file |
为 Compose 文件中的 ${变量} 提供插值来源;不会把文件全部内容自动注入容器。 |
服务下的 environment |
明确设置容器环境变量,可以使用插值结果,也可以直接写固定值。 |
服务下的 env_file |
读取要传入容器的变量文件;相对路径按 Compose 文件所在目录解析。 |
Shell 的同名变量优先于插值文件。显式指定 --env-file 可避免误用默认 .env;本文使用绝对路径固定文件位置。容器侧则是 environment 覆盖服务 env_file,包括显式空值;没有被这些配置覆盖的变量,才可能保留镜像自带的 ENV。Shell 不会自动覆盖写死在 environment 中的值。
用无秘密的小例子看清变量去向
以下是独立演示,/srv/compose-demo 为示例目录,env-demo 为示例项目名,不要覆盖现有业务文件。先在宿主机创建该目录,再分别保存三个文件。镜像首次运行可能需要下载,镜像内含本例使用的 sleep、printenv 程序。
.env:
APP_MODE=staging
container.env:
LOG_LEVEL=info
compose.yaml:
services:
app:
image: busybox:1.37
command: ["sleep", "3600"]
env_file:
- ./container.env
environment:
APP_MODE: "${APP_MODE:-development}"
在宿主机执行 printenv APP_MODE,只查看这个非敏感变量;无输出且退出码为 1 表示未设置。若显示其他值,它会优先参与本例插值。确认该宿主机变量不需要传入本次演示后,可在当前终端执行 unset APP_MODE 再继续。
第一步:只校验配置,不打印全部变量
在宿主机执行:
docker compose --project-directory /srv/compose-demo -p env-demo \
-f /srv/compose-demo/compose.yaml \
--env-file /srv/compose-demo/.env config --quiet
无错误退出只表示配置校验通过,并不能证明运行中的容器已更新。不要为了找一个变量,把完整 config、env 或容器环境列表粘贴到工单里,其中可能包含密码和令牌。
第二步:创建或定点重建,再看容器实际值
下面两条均在宿主机执行;最后的 printenv 在演示容器内运行:
docker compose --project-directory /srv/compose-demo -p env-demo \
-f /srv/compose-demo/compose.yaml \
--env-file /srv/compose-demo/.env up -d --no-deps --force-recreate app
docker compose --project-directory /srv/compose-demo -p env-demo \
-f /srv/compose-demo/compose.yaml \
--env-file /srv/compose-demo/.env exec -T app printenv APP_MODE LOG_LEVEL
当宿主机没有覆盖值时,预期依次得到 staging 和 info:前者经 environment 注入,后者来自服务 env_file。这里只验证已知的非敏感变量。实际业务镜像未必提供 printenv 或 shell,应使用应用已有的单项诊断方式,不要因命令缺失就断言变量没有注入。
如果仅在 .env 新增 OTHER_FLAG=on,服务配置既没有引用它,也没有把该文件声明为服务 env_file,这个变量不会自动进入容器。文件名不能决定用途:同一文件可以被两个入口分别读取,但每个入口的作用仍需分开判断。
仍然不对时,按这个顺序检查
- 值被覆盖:先查宿主机是否导出了同名变量,再查服务 environment 是否覆盖 env_file;不要凭 .env 的文件内容猜运行值。
- 空值与未设置混淆:
${APP_MODE:-development}在未设置或空字符串时都取默认值;${APP_MODE-development}只在未设置时取默认值。直接写APP_MODE: ""会向容器传入空字符串。 - 只执行了 restart:
docker compose restart重启现有容器,不加载新环境配置。修改后要让 Compose 创建新容器,随后再次定点检查。 - 操作了另一个项目:业务环境应使用原项目名、原项目目录及全部原有
-f文件,不能把演示项目名和路径直接套上去。
单服务重建会中断该服务;--no-deps 用于避免同时启动其依赖。通常会继续挂载原有数据卷,但容器可写层会随旧容器移除。操作前检查挂载和回退方案,不要额外加删除卷参数。