目錄
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裡的值。app.env中的GREETING="hello world"經過 Compose 讀取後沒有引號,和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日