--env-file 和 env_file 的区别
这是关于 Docker Compose 中 --env-file (CLI 参数) 和 env_file (YAML 配置) 的深度对比总结。理解它们的区别是解决环境变量问题的关键。
1. 核心区别一览表
| 特性 | --env-file (CLI 参数) | env_file (YAML 配置) |
|---|---|---|
| 全称 | docker compose --env-file <path> | services.service_name.env_file |
| 执行阶段 | 解析阶段 (Parse Time) 在 Docker Compose 读取 yml 之前 | 运行阶段 (Runtime) 在 Docker 创建容器时 |
| 执行者 | Docker Compose 客户端 (CLI) | Docker 引擎 (Engine) |
| 主要用途 | 替换 YAML 中的变量 例如: ${PROJECT_HOME}, ${TAG} | 注入容器内部环境变量 例如:程序读取的 DB_PASSWORD |
| 作用范围 | 全局 影响整个 Compose 项目的所有服务 | 局部 默认仅影响当前服务 (V2.20+ 支持全局) |
| 默认行为 | 自动加载 .env 如果不指定,CLI 默认只读 .env | 无默认 必须显式在 yml 中写出文件名 |
| 文件位置 | 相对于 运行命令的目录 | 相对于 docker-compose.yml 所在目录 |
2. 生效时间与流程图解
为了更直观,我们可以把 docker compose up 的过程分为两步:
--env-file作用于 阶段一。如果这一步没读到变量,YAML 里的${VAR}就会保持原样(导致路径错误或报错)。env_file作用于 阶段二。如果这一步没配置,容器内部就读取不到变量(导致程序报错)。
3. 优先级规则 (Priority)
这是最容易混淆的地方,分为 变量替换优先级 和 容器环境变量优先级。
A. YAML 变量替换优先级 (${VAR})
当 Docker Compose 尝试替换 docker-compose.yml 中的 ${VAR} 时,优先级如下:
- 当前 Shell 环境变量 (最高优先级)
- 例如:你在终端执行了
export PROJECT_HOME=/tmp
- 例如:你在终端执行了
--env-file指定的文件- 例如:
docker compose --env-file .env.prod ...
- 例如:
- 默认的
.env文件- 仅当未指定
--env-file时,CLI 才会自动找.env
- 仅当未指定
- YAML 中的默认值
- 例如:
${PROJECT_HOME:-/default/path}
- 例如:
结论:如果你想用
.env.prod替换 YAML 变量,必须用--env-file .env.prod或者导出到 Shell,光在 YAML 里写env_file是没用的。
B. 容器内部环境变量优先级 (Container ENV)
当容器启动,程序读取 os.getenv() 时,优先级如下:
docker compose run -e(命令行临时覆盖)- YAML 中的
environment字段- 例如:
environment: - DB_PASS=123
- 例如:
- YAML 中的
env_file字段- 例如:
env_file: - .env.prod(后加载的覆盖先加载的)
- 例如:
- Dockerfile 中的
ENV指令 (最低优先级)
结论:
env_file的优先级低于environment。如果你同时在两者里定义了同一个变量,environment胜出。
4. 常见场景与最佳实践
场景 1:多环境部署 (开发/测试/生产)
目标:同一套 docker-compose.yml,通过不同文件控制路径和密码。
- 做法:
- 准备
.env.dev和.env.prod。 - YAML 中变量使用
${VAR}语法。 - YAML 中
env_file也使用变量引用(高级用法)或固定指向。 - 启动命令区分:bash
# 开发环境 docker compose --env-file .env.dev up -d # 生产环境 docker compose --env-file .env.prod up -d
- 准备
场景 2:敏感信息隔离
目标:.env 文件提交到 git,但密码不提交。
- 做法:
.env存放非敏感配置(如PROJECT_HOME),提交到 git。.env.secret存放密码,加入.gitignore。- YAML 中:yaml
services: db: environment: - DB_PASSWORD=${DB_PASSWORD} # 从 CLI 解析阶段获取 # 或者 env_file: - .env.secret # 从运行阶段获取 - 启动时:
docker compose --env-file .env.secret up -d
场景 3:默认值兜底
目标:如果没有配置文件,也能启动。
- 做法:在 YAML 中使用默认值语法。yaml如果
volumes: - ${PROJECT_HOME:-/var/www/html}:/app.env或--env-file里没定义PROJECT_HOME,则使用/var/www/html。
5. 避坑指南 (Checklist)
- 文件名陷阱:
- ❌ 以为
env_file: .env.prod能让 YAML 里的${VAR}生效。 - ✅ 必须用
--env-file .env.prod才能让 YAML 里的${VAR}生效。
- ❌ 以为
- 路径陷阱:
--env-file路径相对于 当前终端所在目录。env_file(YAML) 路径相对于 docker-compose.yml 所在目录。
- 格式陷阱:
- 环境变量文件里不要写
export。 - 不要 在等号周围加空格 (
KEY=VALUE对,KEY = VALUE错)。 - 值如果包含特殊字符,建议加引号 (
KEY="VALUE").
- 环境变量文件里不要写
- 缓存陷阱:
- 修改了
.env文件后,docker compose up不会自动重载配置。 - ✅ 建议先运行
docker compose config确认变量已替换,再up -d。 - ✅ 如果容器已存在,可能需要
docker compose down再up才能生效某些配置。
- 修改了
一句话总结
--env-file是给 Docker Compose 工具 看的,用来生成最终的配置文件(替换${})。env_file是给 容器里的程序 看的,用来运行时读取配置。.env是唯一一个不用写参数就能被 Docker Compose 工具 自动识别的文件。