Docker Compose 完全指南 / 第 6 章 · 环境变量:.env 文件、environment 与 env_file
第 6 章 · 环境变量管理
6.1 环境变量的重要性
环境变量是容器化应用中配置外部化的核心手段,遵循 12-Factor App 方法论。
为什么用环境变量?
| 原则 | 说明 |
|---|---|
| 配置与代码分离 | 同一份镜像,不同环境用不同配置 |
| 无状态 | 容器可随时替换,配置不绑定容器 |
| 安全 | 敏感信息不硬编码在代码或镜像中 |
| 灵活性 | 运行时覆盖,无需重新构建 |
6.2 环境变量的四种来源
Compose 中环境变量有四个来源,按优先级从高到低排列:
优先级:高 → 低
┌─────────────────────────────────┐
│ 1. docker compose run -e │ 命令行注入
│ 或 shell 环境变量 │
├─────────────────────────────────┤
│ 2. environment: 指令 │ compose.yaml 中直接指定
├─────────────────────────────────┤
│ 3. env_file: 指令 │ 从文件加载
├─────────────────────────────────┤
│ 4. Dockerfile ENV │ 镜像构建时设置
└─────────────────────────────────┘
⚠️ 注意:高优先级的值会覆盖低优先级的值。理解这个顺序对调试配置问题至关重要。
6.3 environment 指令
在 compose.yaml 中直接定义环境变量。
两种语法
services:
app:
image: myapp:latest
# 语法一:映射格式(推荐,更清晰)
environment:
NODE_ENV: production
DATABASE_URL: postgresql://user:pass@db:5432/mydb
LOG_LEVEL: info
CACHE_TTL: "3600" # 数字值建议加引号
# 语法二:列表格式
# environment:
# - NODE_ENV=production
# - DATABASE_URL=postgresql://user:pass@db:5432/mydb
# - LOG_LEVEL=info
空值处理
services:
app:
environment:
# 显式设置为空字符串
DEBUG: ""
# 从宿主机继承(仅映射格式支持)
HOME: # 继承宿主机的 $HOME
USER: # 继承宿主机的 $USER
PATH: /usr/local/bin:/usr/bin:/bin # 可覆盖
💡 映射格式中,如果值为空,Compose 会从宿主机环境中继承同名变量。列表格式中
KEY=会设置为空字符串,KEY(无等号)才会继承。
动态值插值
services:
app:
environment:
# 引用 .env 文件或 shell 中的变量
DATABASE_URL: postgresql://${DB_USER}:${DB_PASS}@db:5432/${DB_NAME}
# 带默认值
LOG_LEVEL: ${LOG_LEVEL:-info}
PORT: ${PORT:-3000}
# 仅在变量存在时使用(否则为空)
EXTRA_CONFIG: ${EXTRA_CONFIG:-}
6.4 .env 文件
.env 文件用于定义 Compose 文件本身使用的变量(注意不是容器内的环境变量)。
默认行为
Compose 自动加载项目根目录下的 .env 文件(如果存在)。
# 项目结构
myproject/
├── .env # ← Compose 自动加载
├── compose.yaml
└── ...
.env 文件语法
# .env — 注意:这不是 shell 脚本,但语法类似
# 注释行
# 空行会被忽略
# 基本赋值
DB_USER=admin
DB_PASS=s3cretP@ss
DB_NAME=myapp
# 值包含空格时必须加引号
APP_TITLE=My Application
# 单引号(不解释变量)
GREETING='Hello $USER'
# 双引号(解释变量)
FULL_GREETING="Hello $USER"
# 可用于端口、版本等
POSTGRES_VERSION=16
WEB_PORT=8080
在 compose.yaml 中使用 .env 变量
# .env
# DB_USER=admin
# DB_PASS=s3cretP@ss
# DB_NAME=myapp
# compose.yaml
services:
db:
image: postgres:${POSTGRES_VERSION:-16}-alpine
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASS}
POSTGRES_DB: ${DB_NAME}
web:
image: myapp:latest
ports:
- "${WEB_PORT:-8080}:3000"
environment:
DATABASE_URL: postgresql://${DB_USER}:${DB_PASS}@db:5432/${DB_NAME}
.env 与 environment 的区别
| 维度 | .env | environment: |
|---|---|---|
| 用途 | Compose 文件中的变量替换 | 容器内的环境变量 |
| 加载时机 | Compose 读取 YAML 时 | 容器启动时 |
| 谁使用 | Compose CLI | 容器内进程 |
| 示例 | ${WEB_PORT} → 8080 | NODE_ENV: production |
# 关键区别演示
services:
app:
ports:
- "${WEB_PORT}:3000" # ← .env 变量,由 Compose 解析
environment:
PORT: "3000" # ← 容器环境变量,传入容器
DB_HOST: db # ← 容器环境变量
6.5 env_file 指令
env_file 将文件中的变量直接注入为容器环境变量,适合管理大量变量。
基本用法
services:
app:
image: myapp:latest
env_file:
- app.env # 相对路径(相对于 compose.yaml)
- common.env
# app.env
NODE_ENV=production
LOG_LEVEL=info
DATABASE_URL=postgresql://user:pass@db:5432/mydb
API_KEY=sk-abc123xyz
多 env_file 与合并
services:
app:
env_file:
- common.env # 通用配置(先加载)
- app.env # 应用配置(后加载,会覆盖同名变量)
- secrets.env # 敏感配置(最后加载)
加载顺序:文件按列表顺序加载,后面的文件会覆盖前面同名的变量。
env_file 高级选项
services:
app:
env_file:
- path: app.env # 长语法
required: true # 必须存在,否则报错(Compose V2.24+)
- path: optional.env
required: false # 可选,不存在不报错
env_file 语法注意事项
# app.env
# ✅ 正确
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
# ✅ 值包含空格
APP_NAME=My Application
# ✅ 引号(引号会成为值的一部分 ⚠️)
DB_PASS="s3cret" # 容器内值为 "s3cret"(含引号!)
DB_PASS2='s3cret' # 同上
# ❌ 不支持变量引用
HOME_DIR=$HOME # 字面值 "$HOME",不会展开
# ❌ 不支持 export
export DB_HOST=... # 会将 "export DB_HOST" 作为变量名
⚠️ 易错点:
env_file中的引号不会被去除。DB_PASS="s3cret"传入容器的值是"s3cret"(包含双引号),而不是s3cret。大多数情况下不应使用引号。
6.6 优先级实验
通过以下实验理解优先级:
# compose.yaml
services:
demo:
image: alpine:3.20
command: ["sh", "-c", "echo VAR=$VAR && sleep 3600"]
env_file:
- demo.env
environment:
VAR: "from-environment"
# demo.env
VAR=from-env-file
# 实验 1:使用 environment 值
docker compose up
# 输出: VAR=from-environment
# 实验 2:通过命令行注入(更高优先级)
VAR=from-shell docker compose up
# 输出: VAR=from-shell
# 实验 3:run -e 注入
docker compose run -e VAR=from-run-flag demo
# 输出: VAR=from-run-flag
优先级汇总表
| 来源 | 优先级 | 示例 |
|---|---|---|
docker compose run -e | 🥇 最高 | docker compose run -e K=V app |
| Shell 环境变量 | 🥈 | K=V docker compose up |
environment: 指令 | 🥉 | environment: {K: V} |
env_file: 指令 | 4 | env_file: app.env |
Dockerfile ENV | 5(最低) | ENV K=V |
6.7 变量替换(Interpolation)
Compose 文件中可以使用 ${VAR} 或 $VAR 语法引用变量。
语法大全
services:
app:
environment:
# 基本引用
DB_HOST: ${DB_HOST}
# 带默认值(变量未设置时使用)
DB_PORT: ${DB_PORT:-5432}
# 带默认值(变量为空或未设置时使用)
DB_NAME: ${DB_NAME:-myapp}
# 错误提示(变量未设置时报错)
DB_PASS: ${DB_PASS:?数据库密码必须设置}
# 仅在变量为空或未设置时使用默认值
LOG_LEVEL: ${LOG_LEVEL-default}
# 仅在变量未设置时使用默认值(空字符串时不触发)
CACHE_TTL: ${CACHE_TTL-3600}
替换操作符对比
| 操作符 | 变量未设置 | 变量为空字符串 "" | 变量有值 |
|---|---|---|---|
${VAR} | 空 | 空 | 变量值 |
${VAR:-default} | default | default | 变量值 |
${VAR-default} | default | 空 | 变量值 |
${VAR:?error} | 报错退出 | 报错退出 | 变量值 |
${VAR?error} | 报错退出 | 空 | 变量值 |
💡 建议:大多数场景使用
${VAR:-default}最安全——未设置和空值都用默认值。
关闭变量替换
如果 compose.yaml 中包含 $ 字符但不想被替换(如 shell 脚本),使用 $$ 转义:
services:
app:
entrypoint: /bin/sh -c
command:
- |
echo "Literal dollar: $$HOME"
# 不会被 Compose 替换,传入容器后由 shell 解释
6.8 多环境管理模式
模式一:多个 .env 文件
project/
├── .env # 默认(开发)
├── .env.staging # 预发布
├── .env.production # 生产
├── compose.yaml
└── compose.prod.yaml
# 开发环境
docker compose up
# 生产环境
docker compose -f compose.yaml -f compose.prod.yaml --env-file .env.production up -d
模式二:env_file 分层
services:
app:
image: myapp:latest
env_file:
- env/common.env # 通用配置
- env/${APP_ENV:-dev}.env # 环境特定配置
# env/common.env
LOG_FORMAT=json
APP_NAME=myapp
# env/dev.env
LOG_LEVEL=debug
DATABASE_URL=postgresql://dev:dev@localhost:5432/myapp_dev
# env/prod.env
LOG_LEVEL=warn
DATABASE_URL=postgresql://prod:prod@db:5432/myapp
模式三:变量组合
# compose.yaml — 通用基础配置
services:
app:
image: ${DOCKER_REGISTRY:-docker.io}/${DOCKER_REPO:-myorg}/myapp:${APP_VERSION:-latest}
ports:
- "${APP_PORT:-8080}:3000"
environment:
NODE_ENV: ${NODE_ENV:-development}
LOG_LEVEL: ${LOG_LEVEL:-debug}
DATABASE_URL: ${DATABASE_URL}
REDIS_URL: ${REDIS_URL:-redis://cache:6379}
6.9 敏感信息处理
❌ 不推荐:直接写在 compose.yaml
# 不要这样做!compose.yaml 会进入版本控制
services:
db:
environment:
POSTGRES_PASSWORD: mysecretpassword # ❌ 密码明文
⚠️ 可接受:使用 .env 文件
# compose.yaml
services:
db:
environment:
POSTGRES_PASSWORD: ${DB_PASS}
# .env(加入 .gitignore)
DB_PASS=mysecretpassword
# .gitignore
.env
.env.*
!.env.example
# .env.example(提交到版本控制,作为模板)
DB_USER=
DB_PASS=
DB_NAME=myapp
✅ 推荐:Docker Secrets(Swarm 模式)
services:
db:
image: postgres:16
secrets:
- db_password
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt
💡 详见第 10 章「敏感信息管理」。
6.10 容器内读取环境变量
应用代码中读取
# Python
import os
db_url = os.environ.get("DATABASE_URL", "sqlite:///default.db")
log_level = os.getenv("LOG_LEVEL", "info")
// Node.js
const dbUrl = process.env.DATABASE_URL || "sqlite:///default.db";
const port = parseInt(process.env.PORT || "3000");
// Go
import "os"
dbURL := os.Getenv("DATABASE_URL")
if dbURL == "" {
dbURL = "sqlite:///default.db"
}
运行时查看环境变量
# 查看容器的所有环境变量
docker compose exec app env
# 查看特定变量
docker compose exec app printenv DATABASE_URL
# 在 entrypoint 脚本中调试
docker compose exec app sh -c 'echo $DATABASE_URL'
环境变量与配置文件的配合
services:
nginx:
image: nginx:alpine
volumes:
- ./nginx.template:/etc/nginx/templates/default.conf.template:ro
environment:
BACKEND_HOST: app
BACKEND_PORT: "3000"
SERVER_NAME: myapp.example.com
# nginx.template — Nginx 官方镜像支持 envsubst 自动替换
server {
listen 80;
server_name ${SERVER_NAME};
location / {
proxy_pass http://${BACKEND_HOST}:${BACKEND_PORT};
}
}
💡 Nginx 官方镜像启动时会自动将
/etc/nginx/templates/下的模板文件中的${}变量替换为环境变量值。
6.11 常见问题排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 变量值为空 | 变量名拼写错误 | docker compose config 检查解析结果 |
| 值包含引号 | env_file 中加了引号 | 去除引号 |
| 变量未生效 | 优先级被覆盖 | 检查 environment > env_file > Dockerfile |
.env 未加载 | 文件名错误或不在项目根目录 | 确认文件名和位置 |
? 操作符报错 | 变量未设置且用了 :? | 提供变量或改用 :- |
| 特殊字符问题 | $、# 等字符被解释 | 使用 $$ 转义或单引号 |
调试命令
# 查看完整的解析后配置(含变量替换结果)
docker compose config
# 查看特定服务的环境变量
docker compose config --format json | jq '.services.app.environment'
# 验证 .env 加载
docker compose config | grep "VAR_NAME"
6.12 小结
| 概念 | 说明 |
|---|---|
environment: | 在 compose.yaml 中定义容器环境变量 |
env_file: | 从文件批量加载容器环境变量 |
.env | Compose 文件变量替换用,非容器环境变量 |
| 优先级 | -e > shell > environment > env_file > Dockerfile |
| 变量替换 | ${VAR:-default} 语法,在 compose.yaml 中使用 |
| 安全实践 | .env 加入 .gitignore,提供 .env.example 模板 |
扩展阅读
上一章:第 5 章 · 数据卷 ← | 下一章:第 7 章 · 依赖与健康检查 →