目录
Docker 环境变量教程:配置 .env 与容器环境
同一个镜像在开发、测试和生产环境中可能需要连接不同的数据库、使用不同的设置。最常见的做法是把这些设置放在环境变量中,运行容器时再传入。
使用 -e 设置
$ docker run --rm -e NODE_ENV=production -e PORT=8080 alpine:3.23 sh -c "echo \$NODE_ENV \$PORT"
production 8080
$ export API_KEY=abc123 && docker run --rm -e API_KEY alpine:3.23 sh -c "echo API_KEY=\$API_KEY"
API_KEY=abc123
-e 名称=值直接设置变量。- 只写
-e 名称(不写值)会沿用主机上当前同名的环境变量。
使用 --env-file 读取文件
变量较多时,写在命令中很难管理。可以把它们放进文件:
$ cat app.env
# app.env
NODE_ENV=production
PORT=8080
GREETING="hello world"
DATABASE_URL=postgres://app:secret@db:5432/app
$ docker run --rm --env-file app.env alpine:3.23 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=e311730363f7
NODE_ENV=production
PORT=8080
GREETING="hello world"
DATABASE_URL=postgres://app:secret@db:5432/app
HOME=/root
注意 GREETING 这一行:文件中写的是 "hello world",容器中取得的值包含引号。docker run --env-file 的规则是:
- 每行一个
KEY=VALUE,以#开头的行是注释。 - 值不要加引号,引号会被当作值的一部分。
- 不支持
$OTHER_VAR变量展开。
(下面会看到,Compose 读取同一个文件时会去掉引号,两者的行为不同。)
在 Dockerfile 中设置默认值
ENV NODE_ENV=production \
PORT=3000
ENV 设置的是默认值,docker run -e 可以覆盖它。适合放置“不太会变,也不是机密”的设置。
优先级
从高到低:
docker run -edocker run --env-file- Dockerfile 中的
ENV - 基础镜像中的
ENV
环境变量能被看到吗?
环境变量很方便,但并不安全。任何能运行 docker inspect 的人都能看到:
$ docker run -d --name envdemo -e SECRET_TOKEN=abc123 alpine:3.23 sleep 60
431168d97dc8187ec79e420638155b2f53fd78a9d82f8a6d13eb6bd9aec09bcb
$ docker inspect --format '{{json .Config.Env}}' envdemo
["SECRET_TOKEN=abc123","PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"]
$ docker rm -f envdemo
envdemo
此外,程序出错时可能会把环境变量打印到日志中,子进程也会继承它们。因此,密码和 API 密钥请参考下面“处理机密信息”一节中的建议。
Docker Compose 中的环境变量
Compose 中有两种容易混淆的“环境变量”,下面用一个示例说明。compose.yaml:
services:
app:
image: alpine:3.23
command: ["sh", "-c", "env | sort | grep -E 'APP_|DATABASE|NODE_ENV|PORT|GREETING'"]
environment:
APP_VERSION: ${APP_VERSION}
DATABASE_PASSWORD: ${DB_PASSWORD}
env_file:
- app.env
1. 传给容器的变量:environment 与 env_file
environment 直接写在 compose.yaml 中;env_file 从文件读取(这里沿用上面的 app.env)。
2. 用于替换 compose.yaml 内容的变量:.env
Compose 会自动读取项目文件夹中的 .env 文件,用来替换 compose.yaml 中的 ${...}。使用 docker compose config 可以查看替换后的最终配置:
$ cat .env
APP_VERSION=1.4.2
DB_PASSWORD=change-me
$ docker compose config app
name: ch15
services:
app:
command:
- sh
- -c
- env | sort | grep -E 'APP_|DATABASE|NODE_ENV|PORT|GREETING'
environment:
APP_VERSION: 1.4.2
DATABASE_PASSWORD: change-me
DATABASE_URL: postgres://app:secret@db:5432/app
GREETING: hello world
NODE_ENV: production
PORT: "8080"
image: alpine:3.23
networks:
default: null
networks:
default:
name: ch15_default
$ docker compose run --rm app
[+] run 1/1
✔ Network ch15_default Created 0.0s
Container ch15-app-run-dfbcf95b7271 Creating
Container ch15-app-run-dfbcf95b7271 Created
APP_VERSION=1.4.2
DATABASE_PASSWORD=change-me
DATABASE_URL=postgres://app:secret@db:5432/app
GREETING=hello world
NODE_ENV=production
PORT=8080
${APP_VERSION}、${DB_PASSWORD}被替换成了.env中的值。- Compose 读取
app.env中的GREETING="hello world"后会去掉引号,这与docker run --env-file不同。 PORT: "8080"加上了引号,表示它是字符串(环境变量一律是字符串)。
.env 本身不会自动传入容器,它只用于替换 compose.yaml。要将变量传入容器,必须在 environment 中引用(如 APP_VERSION: ${APP_VERSION}),或者使用 env_file: .env。
默认值与必填检查
| 写法 | 含义 |
|---|---|
${VAR} | 替换为 VAR 的值 |
${VAR:-默认值} | 未设置或为空时使用默认值 |
${VAR:?错误消息} | 未设置或为空时直接报错 |
${VAR:?} 很适合用于密码这类“必须设置”的变量。忘记设置时会立即停止,而不是使用空密码启动:
services:
db:
image: postgres:18
environment:
POSTGRES_PASSWORD: ${MISSING_PASSWORD:?请在 .env 中设置 MISSING_PASSWORD}
$ docker compose -f compose.required.yaml config
error while interpolating services.db.environment.POSTGRES_PASSWORD: required variable MISSING_PASSWORD is missing a value: 请在 .env 中设置 MISSING_PASSWORD
处理机密信息
- 不要把密码写进 Dockerfile 的
ENV或ARG,它们会留在镜像中(ARG的实测见 Dockerfile 指令大全)。 - 不要将
.env提交到 Git。在.gitignore中加入.env,另外提供一个不含真实值的.env.example。 - 在
.dockerignore中加入.env,避免它被COPY . .打包进镜像。
通过文件传递机密信息(Compose secrets)
许多官方镜像支持以 _FILE 结尾的变量,从文件中读取密码:
services:
db:
image: postgres:18
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
$ docker compose up -d db
[+] up 1/1
✔ Container ch15-db-1 Started 0.3s
$ docker compose exec db sh -c "ls -l /run/secrets; cat /run/secrets/db_password; echo; env | grep POSTGRES"
total 4
-rw-rw-r-- 1 1001 1001 16 Oct 12 14:25 db_password
s3cret-from-file
POSTGRES_PASSWORD_FILE=/run/secrets/db_password
$ docker compose down
[+] down 2/2
✔ Container ch15-db-1 Removed 0.1s
✔ Network ch15_default Removed 0.1s
机密内容以文件形式出现在容器的 /run/secrets/db_password 中,环境变量里只有文件路径 POSTGRES_PASSWORD_FILE,docker inspect 看不到密码本身。
构建时需要的机密信息
例如,安装私有包需要 token,请使用 BuildKit 的 secret 挂载:
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=npm_token \
NPM_TOKEN=$(cat /run/secrets/npm_token) npm ci
docker build --secret id=npm_token,env=NPM_TOKEN -t myapp .
token 只在这条 RUN 命令执行期间存在,不会留在任何层中。
重点回顾
- 优先级:
-e/environment>--env-file/env_file> DockerfileENV。 .env用于替换 compose.yaml 中的${VAR},不会自动传入容器。docker run --env-file不会去掉值中的引号,Compose 会。- 通过文件(secrets)传递机密信息,不要把它们写进镜像。
动手练习
练习 1:镜像中有 ENV MODE=prod,使用 docker run -e MODE=dev 运行时,容器中的 MODE 是什么?
答案
dev。-e 的优先级高于 Dockerfile 的 ENV。
练习 2:compose.yaml 中写了 POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required},但 .env 中没有 DB_PASSWORD。运行 docker compose up 会怎样?
答案
Compose 会直接报错并停止,显示 DB_PASSWORD is required,不会使用空密码创建数据库。
常见问题
修改 .env 后,容器的环境变量没有变化?
环境变量在创建容器时确定。修改后需要重建容器:Compose 使用 docker compose up -d,restart 不会应用更改。
用 docker inspect 能看到我的密码吗?
能。通过 -e 传入的值都会出现在 docker inspect 中,敏感数据请改用文件(secrets)方式传递。
${VAR:-默认值} 和 ${VAR:?消息} 有什么区别?
前者在未设置时使用默认值;后者在未设置时直接报错并停止,适用于必填密码,避免值变成空字符串。
更新于 2026年10月14日