Docker Compose 的 env_file 與 ${VAR} 怎麼分?解決變數插值為空
你可能遇過這種 Docker Compose 錯誤:runtime.env 明明有 IMAGE_TAG,但 compose.yaml 裡的 ${IMAGE_TAG} 還是變成空字串,最後得到一個無效的 image reference。原因通常不是檔案沒有讀到,而是把兩種不同階段的 environment file 當成同一個設定。
直接記法是:services.<name>.env_file 把值送進 container;.env、CLI --env-file 或 shell 才是 Compose 在解析 ${VAR} 時使用的插值來源。 先用 docker compose config 看解析後的 model,再啟動服務。
env_file 和 --env-file 的工作位置不同
| 設定 | 作用階段 | 適合解決的問題 |
|---|---|---|
services.web.env_file | container 建立後的執行環境 | 把 PORT、LOG_LEVEL 或 runtime 設定交給 web |
專案 .env | Compose 解析 model 時的預設插值來源 | 讓 image: "web:${TAG}" 找到 TAG |
docker compose --env-file ... | Compose CLI 解析 model 時的明確輸入 | CI、staging、production 使用不同的插值檔 |
| shell environment | Compose 解析 model 時的最高優先來源 | 一次性的部署覆寫或 CI 明確傳值 |
env_file 是 service 的設定,不會在 Compose 已經解析 image、volume、port 或 build argument 之後回頭補值。這也是為什麼只改 runtime.env,卻看不到 image: "my-web:${IMAGE_TAG}" 改變。
先重現「容器有值、model 沒值」的情境
下面的設定把兩個階段放在一起,正好能看出問題:
services: web: env_file: - ./runtime.env image: "my-web:${IMAGE_TAG}"runtime.env 可以讓 web container 在啟動後讀到 IMAGE_TAG,但 Compose 要先在建立 application model 時解析 image。若執行指令的 shell、CLI --env-file 或 project .env 沒有 IMAGE_TAG,${IMAGE_TAG} 可能被警告後替換成空字串。
用 .env 或 --env-file 提供插值來源
專案預設使用 .env
IMAGE_TAG=2026.08PUBLIC_ORIGIN=https://example.comservices: web: image: "my-web:${IMAGE_TAG}" environment: PUBLIC_ORIGIN: "${PUBLIC_ORIGIN}"Compose 沒有明確傳入 --env-file 時,會依 project directory 的規則尋找 .env。如果使用 -f、--project-directory 或 COMPOSE_FILE,專案目錄可能不是你目前 shell 所在的位置,所以不要只看檔案是否存在,要檢查實際解析結果。
CI 或多環境使用 --env-file
把 environment file 放在明確的位置,並在每個工作流寫出來源:
IMAGE_TAG=2026.08-rc1PUBLIC_ORIGIN=https://staging.example.comdocker compose --env-file ./config/.env.ci configdocker compose --env-file ./config/.env.ci up -d--env-file 是 Compose CLI 的輸入;它和 services.web.env_file 不同。若兩者都存在,可以讓前者解析 image tag,後者把 container 啟動後才需要的額外值交給服務。
需要值時,用 required expression 讓它提早失敗
如果空字串會產生無效 image、錯誤 host path 或連錯資料庫,不要讓錯誤拖到 container 啟動後才出現:
services: web: image: "my-web:${IMAGE_TAG:?請先設定 IMAGE_TAG}" environment: PUBLIC_ORIGIN: "${PUBLIC_ORIGIN:?請先設定 PUBLIC_ORIGIN}"需要預設值時才使用 :-:
services: web: environment: LOG_LEVEL: "${LOG_LEVEL:-info}"${VAR:-default} 會把未設定或空字串都視為缺少;${VAR?error} 只檢查是否已設定。選擇哪一種,要看空值是否與你的部署契約相容。
先看 Compose 真正使用的值
不要直接把完整設定檔倒進 CI log,因為解析後的 model 可能包含密碼或連線字串。先看插值環境,再針對非敏感欄位檢查:
set -euo pipefail
docker compose --env-file ./config/.env.ci config --environmentdocker compose --env-file ./config/.env.ci config > /tmp/compose.resolved.yamlrg 'image:|PUBLIC_ORIGIN:|LOG_LEVEL:' /tmp/compose.resolved.yamldocker compose config --environment 會列出 Compose 用來插值的環境;docker compose config 則輸出解析後的 model。兩個指令要和 CI 使用相同的工作目錄、-f 檔案與 --env-file 參數,否則你驗證的是另一份設定。
若設定拆在多個 Compose application,先參考 Compose include 的相對路徑、變數與名稱衝突。若問題是 app 太早連資料庫,則看 healthcheck 與 service_healthy 的啟動順序;那是服務就緒問題,不是變數插值問題。
不要把 Compose CLI 行為套到 Swarm
Docker 官方文件特別註明,.env 的 substitution 是 Docker Compose CLI 的功能,docker stack deploy 不支援同一套插值行為。若最後部署入口是 Swarm,請用實際的 stack deploy 路徑測試,不要因為 docker compose config 成功就推論另一個命令也會得到相同 model。
結論是:用 .env、--env-file 或 shell 解決 ${VAR};用 service env_file 提供 container 的 runtime environment。把這兩層拆開,再用 config --environment 和 config 驗證,Compose 的空 image tag 通常就能在啟動前被抓出來。
常見問題
Q: service 的 env_file 可以填入同一份 Compose 裡的 ${VAR} 嗎?
A: 不要依賴這種行為。service env_file 是給 container runtime environment 的輸入,${VAR} 是 Compose 建立 model 時的插值。請改用 project .env、CLI --env-file 或 shell environment。
Q: Compose 的插值來源哪個優先?
A: 官方插值文件列出的順序是 shell environment 高於 CLI --env-file,而沒有傳入 --env-file 時才使用 project .env。用 docker compose config --environment 檢查實際值,不要用檔案名稱猜 precedence。
Q: 為什麼 docker compose config 只顯示空字串,不直接失敗?
A: 未設定的變數可能被警告後替換成空字串。對必填值使用 ${VAR:?error} 或 ${VAR?error},讓無效 image、路徑或 host 在解析階段就停止。
參考資料:
Docker Docs:Compose variable interpolation
Docker Docs:Set environment variables within your container’s environment
回報錯字、失效連結,或告訴我你想看的延伸主題。