SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-07

Docker Compose 的 .env、env_file 與 environment 差異

了解 Docker Compose 中 .env、env_file 與 environment 的差異、優先順序,以及為何 .env 不會自動載入容器;密碼應改放 secrets。

將 3 種檔案都稱為 env file

Docker Compose 提供 3 種名稱相近但用途不同的機制。.env 檔案會在 Compose 解析檔案前,先填入 compose.yaml 內的 ${VARIABLE} 預留位置。env_file: 屬性會將鍵值配對檔案載入容器的環境。environment: 屬性則會直接在 compose 檔案中設定容器的環境變數。這些機制不可互換。如果其中 2 種機制設定相同的鍵,則由文件定義的優先順序決定最終值。

本指南會逐一示範這些機制,並透過可執行的命令驗證優先順序。接著說明更重要的事項:任何可以執行 docker inspect 的人都能讀取環境變數,因此不應將密碼放入其中。如果你不熟悉 compose 檔案,請先閱讀 VPS 上的 Docker Compose 基礎,再回到這裡了解設定方式。

.env 檔案是供 compose 檔案使用,不是供容器使用

建立一個目錄,並在其中放入兩個檔案。

mkdir -p ~/envdemo && cd ~/envdemo
printf 'ALPINE_TAG=3.20\n' > .env
services:
  demo:
    image: alpine:${ALPINE_TAG}
    command: printenv ALPINE_TAG

現在請 Compose 顯示它實際解析的內容。

docker compose config

輸出會顯示 image: alpine:3.20。placeholder 已消失,因為插值是在解析時完成的。Compose 會在專案目錄中尋找 .env;專案目錄就是存放 compose 檔案的目錄,並替換其中找到的每個 ${NAME}

接著執行服務。

docker compose run --rm demo

printenv ALPINE_TAG 會以狀態 1 結束,且不輸出任何內容。該變數不存在於容器內。這是最常見的誤解:.env 設定的是 compose 檔案,不是程序。單獨建立一個含有 POSTGRES_PASSWORD=hunter2.env 檔案,對資料庫完全不起作用,除非 compose 檔案中的某個部分參照了它。

${NAME:-default} 會在變數未設定或為空時提供預設值。${NAME:?message} 會讓 Compose 拒絕啟動並輸出指定訊息;對於沒有安全預設值的設定,這是正確的選擇。

env_file 將變數載入容器

env_file: 屬性會指定一或多個檔案。檔案內容會成為容器的環境變數。

printf 'GREETING=from_env_file\nAPP_MODE=production\n' > app.env
services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    env_file:
      - ./app.env
docker compose run --rm demo

這會輸出 from_env_file。檔案格式是純 KEY=value 行,每行一個項目;以 # 開頭的內容是註解。這不是 shell 語法。引號在大多數情況下會保留為值的一部分,不需要 export 前綴。= 符號兩側不要加入空格,否則 KEY = value 會產生名稱為 KEY 的變數,且其值會以空格開頭。

找不到 env_file 路徑時會視為錯誤,Compose 會停止。若檔案可能正常不存在,請將其標記為選用:

    env_file:
      - path: ./app.env
        required: false

environment 內嵌設定變數

services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    environment:
      GREETING: from_environment

接受兩種語法:上方的對應形式,以及使用 - GREETING=from_environment 的清單形式。兩者行為完全相同。清單形式還有一項額外功能:不指定值的單獨鍵名,會從執行 docker compose 的 shell 傳遞該變數。

    environment:
      - GREETING
GREETING=from_my_shell docker compose run --rm demo

這會輸出 from_my_shell。如果未在 shell 中設定 GREETING 就執行,Compose 不會設定任何值,也不會顯示警告。這類無訊息的傳遞失敗值得注意,因為服務以空白密碼變數啟動時,通常仍會成功啟動,但實際上完全未受保護。

誰的優先順序較高

Docker 說明了優先順序,從高到低依序為:命令列上的 docker compose run -e,接著是 environmentenv_file,其值會從 shell 或 env 檔案插值取得;然後是 compose 檔案中的一般 environment,再來是 env_file,最後是建置於映像中的 ENV 指示詞。

日常使用時可簡化記憶為:environment: 的優先順序高於 env_file:,而命令列上的 -e 高於兩者。以下在單一檔案中驗證。

services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    env_file:
      - ./app.env
    environment:
      GREETING: from_environment
docker compose run --rm demo
docker compose run --rm -e GREETING=from_cli demo printenv GREETING

第一個指令會輸出 from_environment,表示 environment: 覆寫了 app.env 中的值。第二個指令會輸出 from_cli。compose 檔案中的任何設定都無法覆寫命令列選項。

如果容器的行為看起來像是設定未生效,請勿臆測。docker compose config 會輸出完整解析後的檔案,docker compose config --environment 會輸出 Compose 目前使用的插值變數。多數「我的 env 檔案被忽略」的問題,最後都證實同一個值是在兩個不同層級中重複設定。

環境變數為何會外洩

environment: 中設定密碼後,密碼會儲存在容器的磁碟設定中,docker 群組中的任何使用者都能看到。

docker compose run -d --name leaky -e DB_PASSWORD=hunter2 demo sleep 300
docker inspect leaky --format '{{json .Config.Env}}'

輸出內容會以純文字包含 "DB_PASSWORD=hunter2"。另外還有 3 個路徑會暴露相同的值。docker compose config 會將該值輸出到終端機,因此可能被貼到支援論壇中。容器內的任何程序都能讀取 /proc/1/environ,而且每個子程序都會繼承該變數。應用程式的當機處理程式也經常將完整環境傾印到日誌或錯誤報告中。

docker 群組的成員資格實際上等同於在主機上擁有 root 權限,因此不能將其視為可靠的權限邊界。VPS 上的最小權限使用者帳戶指南說明了為何在任何共用主機上都應限制該群組。

Compose secrets 將值保存在檔案中

Compose 支援以檔案為基礎的 secrets。值會以檔案形式掛載到容器中,而不是注入環境變數。

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
secrets:
  db_password:
    file: ./db_password.txt

secret 會掛載在容器內的 /run/secrets/db_password。斜線後的名稱,是頂層 secrets: 區塊中的 secret 名稱。

_FILE 後綴是 Docker Official Images 採用的慣例,包括 postgresmysqlmariadb。這些 entrypoint 指令碼會檢查 VARNAME_FILE、讀取檔案,並使用其內容。這不是 Docker 的功能,因此只有映像檔實作該慣例時才有效。在假設 SOMETHING_FILE 會受到支援前,請先查看映像檔文件。不支援此慣例的應用程式通常可以在啟動時自行讀取檔案,或傳入檔案路徑,再由自訂的 entrypoint 處理。

從執行中的容器內驗證:

docker compose exec db cat /run/secrets/db_password
docker compose exec db printenv POSTGRES_PASSWORD

第一個指令會印出密碼。第二個指令不會輸出任何內容,因為該值從未進入環境變數。這正是目的:此容器中的 docker inspect 只會顯示無害的路徑。

請保護主機上的來源檔案,因為 secret 的私密性取決於其背後的檔案:

chmod 600 db_password.txt

VPS 上的實用折衷方案

許多自架映像檔不支援 _FILE 變數,因此只能透過環境變數傳入設定。在只有一名管理員的 VPS 上,實際目標是避免這些值存放在專案目錄中任何人都可讀取的檔案裡,並且不要將它們提交至 git。

sudo install -o root -g root -m 600 /dev/null /etc/myapp/app.env
sudo nano /etc/myapp/app.env
    env_file:
      - /etc/myapp/app.env

install -m 600 建立檔案時就會設定檔案模式,因此不會出現所有使用者都能讀取的時間窗口。檔案由 root 擁有,因此主機上的非 root 使用者無法讀取;但任何能執行 docker 的人,仍可從容器中讀出這些值。將 *.env.env 加入 .gitignore,並提交一份 app.env.example,其中只保留金鑰名稱,值則留空。已提交的密碼就必須視為已輪替的密碼。

輪替值意味著重新啟動服務。容器程序啟動時只會讀取一次環境變數,因此編輯檔案後,必須執行 docker compose up -d --force-recreate db 才會生效。VPS 上透過 HTTPS 執行 n8n 指南也採用相同模式,將加密金鑰存放在 compose 檔案之外。

依環境拆分設定

Compose 預設會從專案目錄讀取 .env。使用 --env-file 將其指定到其他位置。

docker compose --env-file .env.staging config

Compose 會依序讀取多個檔案,後讀取的檔案會覆寫先前檔案的設定。將非機密的預設值保存在已提交至版本控制的檔案中,並將 secret 保存在不離開伺服器的檔案中。env_file: 也適用相同規則;若有重複的 key,最後列出的檔案會優先。

FAQ

為什麼容器內的 .env 檔案會被忽略?

它並未被忽略。.env 檔案只會替換 compose 檔案中的 ${NAME} 預留位置,不會設定容器內的變數。若要將值傳入容器,請使用 environment: { KEY: "${NAME}" } 參照;或改用 env_file: ./that-file.env

environment 會覆寫 env_file,還是反過來?

environment: 優先。Docker 的文件化優先順序中,environment 屬性高於 env_file 屬性,而兩者的優先順序都低於命令列上的 docker compose run -e。如果同一個鍵在兩處都有設定,env_file 中的值會靜默地不予使用。

如何查看 Compose 最終會使用的值?

執行 docker compose config,即可輸出套用所有插值後完整解析的 compose 檔案。對於已在執行中的容器,docker inspect <container> --format '{{json .Config.Env}}' 會顯示其程序實際接收到的內容。

Compose secret 會加密嗎?

不會。以檔案為基礎的 secret 會以純文字檔案形式掛載到容器內的 /run/secrets/<name>,來源檔案則未加密地儲存在主機磁碟上。它的優點在於作用範圍,而不是加密:該值不會出現在容器環境、docker inspect 輸出或會列出環境內容的當機傾印中。

env 檔案可以使用引號和空格嗎?

請使用 KEY=value with spaces,並省略引號。Compose 會將該行其餘內容完整視為值,因此引號通常會成為值中的字面字元。= 周圍絕對不要加入空格,否則鍵會帶有結尾空格,導致任何比對都無法成功。