变量替换在 Docker Compose 中需关注加载时机、作用域与覆盖逻辑,仅 service 字段值、environment 值、env_file 路径三处支持 ${VAR};默认值推荐用 ${VAR:-default} 避免空字符串导致失效;env_file 先加载、environment 后覆盖;排错按 docker compose config、shell 与 .env 冲突、env_file 顺序、YAML 格式四步定位。
变量替换在 docker compose 中不是“写对了就能用”,而是必须理解加载时机、作用域和覆盖逻辑。真正卡住人的,往往不是语法写错,而是变量在哪个环节被谁覆盖、又为何没生效。下面直击关键点,不绕弯。
变量引用的三种合法位置与限制
Docker Compose 支持 ${VAR} 语法的位置有限,仅在以下三处有效:
-
service 字段值中:如
image: nginx:${NGINX_TAG}、ports: ["${PORT}:80"] -
environment 列表项的值部分:如
- DB_URL=postgres://${DB_USER}:${DB_PASS}@${DB_HOST}/app -
env_file 文件路径中:如
env_file: - .env.${ENV_MODE:-dev}(需 Compose v2.20+)
⚠️ 注意:service 名称、version、volumes 键名、depends_on 服务名等不支持变量替换。试图写 ${SERVICE_NAME}: 或 version: '${COMPOSE_VERSION}' 会直接报错或静默忽略。
默认值语法的两种行为差异
${VAR:-default} 和 ${VAR-default} 看似一样,但处理空值逻辑不同:
-
${DB_PASS:-secret123}:当DB_PASS=""(空字符串)或未定义时,都使用secret123 -
${DB_PASS-secret123}:仅当DB_PASS完全未设置(unset)才用默认值;若设为空字符串DB_PASS="",则保留空值
推荐日常统一用 :- 形式,避免因脚本意外导出空值导致连接失败(比如 CI 中未配置密码变量,却导出了 DB_PASS=)。
env_file 与 environment 的覆盖关系必须记牢
这是最常误判的优先级陷阱:
-
env_file先加载,提供基础变量 -
environment后应用,同名变量一定覆盖env_file中的值
例如:
env_file:
- .env.shared
environment:
- DEBUG=true
- DB_HOST=${DB_HOST}
即使 .env.shared 里有 DEBUG=false,容器内 DEBUG 一定是 true。这不是 bug,是设计行为。想让文件值生效,就别在 environment 里重复声明该变量。
排错四步定位法(实操有效)
变量不生效?按顺序检查这四步,90% 问题当场定位:
-
确认 Compose 是否读到了 .env:运行
docker compose config,看输出里environment块是否已展开成实际值。若仍是${DB_HOST},说明变量根本没加载(.env 路径错 / 文件名非 .env / 用了 Windows 换行符) -
查变量来源冲突:执行
echo $DB_HOST在宿主机终端,再对比.env内容。若两者不同,且你没加--env参数,那 shell 环境变量会覆盖.env(优先级:shell > .env) -
验证 env_file 加载顺序:多个
env_file按书写顺序加载,后写的文件可覆盖前面同名变量。把敏感/环境特异的文件放最后 -
检查 YAML 格式硬伤:.env 文件中
DB_HOST = localhost(等号前后有空格)、API_KEY=abc#123(# 被当注释)、值含空格未加引号(LOG_LEVEL=debug info→ 应写LOG_LEVEL="debug info")
不复杂但容易忽略


















