Docker Compose 環境變數未生效怎麼辦:.env、env_file、優先順序與重建排查

Docker Compose 修改 .env 後未生效?用可重現範例區分變數插值與容器環境,檢查 env_file、Shell 覆寫、錢字號跳脫及 restart 與重建的差異,附 PowerShell 排錯指令。

Docker Compose 環境變數未生效,先分清兩件事:.env 和 --env-file 為 Compose 解析設定提供變數,服務中的 environment 與 env_file 才負責將變數傳入容器。修改設定後,restart 不會更新既有容器的環境,應先檢查解析結果,再用 up -d 套用變更。

照著部署教學操作時,最常見的情況是:.env 明明寫了值,容器裡卻沒有;改了值又重新啟動,程式仍使用舊位址。 這通常需要沿著「輸入檔案 → Compose 設定 → 容器環境 → 應用程式讀取」逐層排查。

本文適用於 Compose V2 與 Linux 容器,包括 Docker Desktop 的 Linux 容器模式。 文件核對日期為 2026 年 11 月 4 日;指令依據官方文件整理,未在本文環境中執行 Docker 容器。

如果你正在維護 Joplin Server、Syncthing 等自架服務,可以先用下面的獨立範例理解覆寫規則,再檢查實際服務設定。

四種寫法分別將變數送到哪裡

寫法 使用位置 主要用途
.env Compose 專案慣用的輸入檔案 為 ${VAR} 等插值提供值
--env-file ./deploy.env Compose 命令列選項 明確指定插值輸入檔案
env_file: ./app.env 個別服務的設定 將檔案中的變數注入該服務容器
environment: 個別服務的設定 直接設定、引用或覆寫容器變數

同一個檔案可以同時擔任兩種用途,但需要明確宣告。 僅將 .env 放在目錄中,不會自動把其中所有變數傳入所有容器。

例如 .env 中有 APP_MODE=production,但服務沒有任何環境變數宣告,容器通常不會因此取得 APP_MODE。 服務寫了 environment: APP_MODE: "${APP_MODE}",這條傳遞鏈才連接起來。

官方說明見 設定容器環境變數。

用不含業務資料的專案觀察覆寫結果

先確認 CLI 與實際連線的 Docker 主機。

1
2
3
docker compose version
docker context show
docker info

建立獨立目錄 compose-env-lab,在其中新增以下三個檔案。 不要直接將實驗服務加入正式環境的專案。

1
2
3
4
compose-env-lab/
├── compose.yaml
├── .env
└── app.env

compose.yaml:

1
2
3
4
5
6
7
8
9
services:
  probe:
    image: alpine:3.22
    env_file:
      - ./app.env
    environment:
      APP_MODE: "${APP_MODE:-development}"
      APP_LABEL: "${APP_LABEL:?APP_LABEL is required}"
    command: ["sleep", "3600"]

.env:

1
2
3
APP_MODE=production
APP_LABEL=compose-env-lab
ONLY_FOR_COMPOSE=not-in-container

app.env:

1
2
APP_MODE=from-env-file
APP_COLOR=blue

本例沒有連接埠映射、資料卷或特權權限。 映像檔需要從你的 Docker 主機可存取的登錄站拉取;初始實驗結束前保持檔案不變,以便比對。

先在這個目錄執行:

1
2
3
docker compose -p compose-env-lab config -q
docker compose -p compose-env-lab config --environment
docker compose -p compose-env-lab config

config -q 成功只表示設定驗證通過。 config --environment 顯示用於插值的變數;一般 config 顯示合併、解析後的服務模型。 兩者都不能證明容器已建立,更不能證明應用程式已讀取那些值。

這些輸出可能包含密碼,請只在可信任的終端機查看,分享前移除敏感值。 具體選項見 docker compose config。

啟動後只讀取需要核對的變數

1
2
3
docker compose -p compose-env-lab up -d
docker compose -p compose-env-lab ps
docker compose -p compose-env-lab exec probe printenv APP_MODE APP_LABEL APP_COLOR

若啟動 Compose 的終端機沒有同名變數覆寫,預期依序看到:

1
2
3
production
compose-env-lab
blue

APP_MODE 來自 .env 的插值結果,覆寫了 app.env 中的同名項目。 APP_COLOR 透過服務的 env_file 注入。 APP_LABEL 是必填項目,缺少或為空時會在設定解析階段報錯。

再單獨查詢未宣告傳入容器的變數:

1
docker compose -p compose-env-lab exec probe printenv ONLY_FOR_COMPOSE

預期沒有輸出,指令傳回非零狀態。 這是實驗的正常結果,說明 .env 並不是將所有變數注入容器的檔案。

.env 的值為什麼被終端機覆寫

Compose 解析 ${APP_MODE} 時,啟動它的 Shell 若已有同名變數,會優先採用 Shell 的值。 所以「檔案寫的是 production,解析結果卻是 staging」時,先檢查終端機環境。

在 Bash 中可以這樣查看及清除實驗變數:

1
2
3
printenv APP_MODE
unset APP_MODE
docker compose -p compose-env-lab config --environment

PowerShell 的對應操作為:

1
2
3
Get-Item Env:APP_MODE -ErrorAction SilentlyContinue
Remove-Item Env:APP_MODE -ErrorAction SilentlyContinue
docker compose -p compose-env-lab config --environment

清除目前終端機的變數前,確認其他工作不需要這個設定。 IDE 工作、CI 作業與系統服務繼承的環境,也可能和手動開啟的終端機不同。

為減少工作目錄造成的歧義,部署指令可以明確指定設定與輸入檔案:

1
2
docker compose -p myapp -f ./compose.yaml --env-file ./deploy.env config -q
docker compose -p myapp -f ./compose.yaml --env-file ./deploy.env config

將 myapp 換成既有專案名稱,不要在正式部署時隨意改名。 專案名稱會參與容器、網路與預設資料卷的命名,改名可能得到另一組資源。

--env-file 的相對路徑以執行指令時的工作目錄為基準;服務 env_file 的一般相對路徑則以 Compose 檔案位置為基準。 多個 Compose 檔案、--project-directory、COMPOSE_FILE 會增加路徑關係的複雜度,排錯時先使用明確路徑縮小範圍。

同時指定多個插值輸入檔案時,後面的檔案可以覆寫前面的同名項目:

1
docker compose --env-file .env --env-file .env.prod config

Shell 中既有的同名變數仍可能優先。 預設 .env 的搜尋細節應搭配目前的 CLI 版本核對,見 Compose 插值與檔案載入規則。

容器內的優先順序要另外檢查

確認插值輸入後,還要看服務最終如何設定容器變數。 在常見的明確賦值情境中,可以依下列覆寫關係理解:

情境 對結果的影響
docker compose run -e APP_MODE=debug 覆寫這次臨時容器的對應變數
在服務的 environment 中設定 APP_MODE 覆寫服務 env_file 的同名值
服務列出多個 env_file 後面的檔案覆寫前面的同名值
映像檔 Dockerfile 中的 ENV 未被執行時的同名設定覆寫時才保留

這裡的 environment 值本身可能來自 Shell 或 .env 插值。 不要將「值從哪裡解析出來」和「透過哪個服務欄位傳入容器」混成同一步驟。 只寫變數名稱而不給值的透傳寫法,還有未解析時移除變數的語意,排錯時優先使用明確賦值。

可以在實驗中驗證臨時覆寫:

1
docker compose -p compose-env-lab run --rm -e APP_MODE=debug probe printenv APP_MODE

預期輸出 debug。 這建立的是一次性容器,不會修改先前透過 up -d 啟動的 probe 容器。 也不要將 run -e 當成 up 的通用參數。

完整適用範圍與透傳案例見 官方環境變數優先順序說明。

修改檔案後,為什麼 restart 仍是舊值

容器的環境變數在建立容器時決定。 docker compose restart 重新啟動既有容器,不會根據修改後的設定重新建立容器環境。

將實驗 .env 中的 APP_MODE 改為 staging 後,可以依序比對:

1
2
3
docker compose -p compose-env-lab config
docker compose -p compose-env-lab restart probe
docker compose -p compose-env-lab exec probe printenv APP_MODE

若容器原本使用 production,重新啟動後仍應維持該值。 此時設定檔和執行中的容器處於不同狀態。

確認解析結果正確後,套用變更:

1
2
docker compose -p compose-env-lab up -d probe
docker compose -p compose-env-lab exec probe printenv APP_MODE

Compose 通常會偵測設定變更並重建受影響的服務。 確實需要強制重建時,使用:

1
docker compose -p compose-env-lab up -d --force-recreate probe

重建會中斷服務,應安排在業務維護時段執行。 資料庫等相依服務是否也需要處理,應由實際架構決定,不要習慣性重建整個專案。

掛載的資料卷會依設定繼續使用,但容器可寫入層中的臨時修改可能遺失。 操作前應確認業務資料確實存放於持久化位置。 官方行為說明見 restart 與 up。

錢字號、空值與布林值的常見陷阱

讓變數在容器內展開

Compose 檔案中的 $VAR 通常會先由 Compose 處理。 若希望由容器中的 Shell 讀取變數,可使用 $$,並明確呼叫 Shell:

1
2
3
4
5
6
services:
  probe:
    image: alpine:3.22
    environment:
      APP_MODE: "production"
    command: ["sh", "-c", "printf '%s\\n' \"$$APP_MODE\""]

這是一個獨立的短期服務範例,執行後印出值並結束。 $$ 阻止 Compose 在主機階段展開該變數;sh -c 負責在容器內展開。 僅將變數放入不經 Shell 的參數陣列,不會自動產生 Shell 展開行為。

語法依據見 Compose Interpolation。

空字串和「未設定」的意義不同

${VAR:-default} 在變數不存在或為空時都使用預設值。 ${VAR-default} 只在變數不存在時使用預設值。 ${VAR:?message} 適合必須非空的輸入,可避免空位址、空路徑一路傳到執行階段。

不要在未記錄原因的情況下,用空字串暫時遮蔽正式環境設定。 服務中的明確空值可能覆寫原本有效的 env_file 設定。

YAML 中類似布林值的內容建議寫成字串:

1
2
3
environment:
  FEATURE_ENABLED: "false"
  WORKER_COUNT: "2"

這只能確保傳入的是字串。 應用程式將 "false" 解讀為停用,還是將所有非空字串視為真,仍取決於自身的設定解析方式。

設定正確、容器也正確,應用程式為什麼仍未改變

先確認應用程式是否支援這個變數名稱,以及是否有設定檔、命令列參數等更高優先順序的來源。 APP_URL、BASE_URL、PUBLIC_URL 並非通用協定,不能跨專案任意替換。

再檢查讀取時機:部分前端設定在建置映像檔時就寫入靜態檔案,執行時設定環境變數不會重寫既有前端產物。 需要建置參數的專案,應依專案文件設定 build.args 或專用啟動指令碼。

最後核對是否檢查了正確的服務與執行個體。 同一專案的 worker、web、scheduler 可能是三個容器,只更新其中一個不會改變另外兩個。

觀察結果 下一步
config 仍是舊值 檢查輸入檔案路徑、Shell 覆寫及多檔案合併
config 正確,exec printenv 是舊值 檢查專案名稱、Docker context 與容器重建
容器值正確,應用程式行為仍舊 檢查變數名稱、應用程式優先順序、讀取時機及實際存取的執行個體
exec 失敗,容器未執行 先查看 ps -a 與對應服務日誌

在實驗中可以直接執行:

1
2
docker compose -p compose-env-lab ps -a
docker compose -p compose-env-lab logs --tail 100 probe

業務映像檔可能沒有 printenv 或 Shell。 此時使用映像檔提供的診斷指令,或由管理員查看容器設定並移除敏感資訊;不要只為排錯就在正式映像檔中臨時安裝大量工具。

密碼、回復與實驗清理

不要將真實密碼寫進範例儲存庫,也不要完整貼上 config、inspect 或全部 env 輸出。 .env 是純文字檔案,不提供加密功能。 可以提交不含憑證的 .env.example;實際部署檔案應依專案要求限制讀取權限,並排除於版本控制之外。

應用程式支援從檔案讀取憑證時,可以考慮 Compose secrets。 它會將授權的 secret 作為容器內的檔案提供,常見位置是 /run/secrets/<name>。 _FILE 變數只是部分映像檔採用的慣例,並非所有應用程式都自動支援。 本機來源檔案仍需妥善保護,見 Compose secrets 官方說明。

變更正式環境設定前,保存上一版設定與環境輸入,並記錄專案名稱、映像檔版本及指令參數。 回復時還原那些檔案,以相同參數檢查 config -q,再執行 up -d,最後驗證應用程式行為。 設定回復不能取代資料庫備份,也不保證能逆轉應用程式已完成的資料遷移。

實驗結束後,在 compose-env-lab 目錄執行:

1
docker compose -p compose-env-lab down

本例未宣告資料卷,不需要加上 -v。 不要將這條實驗指令改成全域清理操作;空間維護另見 Docker 磁碟空間清理指南。

排錯結束時,應分別確認:解析後的設定符合預期、執行中的容器已取得新值、應用程式實際行為已改變。 這三項對應不同層次,逐層確認才能避免反覆修改 .env 卻找錯原因。