Docker Compose secrets 與 configs 怎麼選?從環境變數改成檔案掛載
Docker Compose 裡的 environment 很方便,但把密碼、API key 或憑證直接放進環境變數,會讓所有能讀取 process environment 的程式都更容易碰到它,也可能在 debug log 或錯誤診斷中被印出。Compose 的 secrets 和 configs 都能把資料掛成檔案,但用途並不相同。
直接答案是:敏感資料用 secrets,一般執行期設定用 configs,非敏感的簡單開關才用 environment;建置期間才需要的 credential 則用 build secret。 這些欄位都要求 service 明確授權,且本機 file: 來源仍受 host 檔案權限與 Compose trust model 影響,不是自動加密的秘密保管庫。
先分清四種設定邊界
| 方式 | 生命週期 | 容器內的形態 | 適合放什麼 | 不要拿來做什麼 |
|---|---|---|---|---|
environment | runtime | 環境變數 | 非敏感設定、*_FILE 路徑 | 密碼、私鑰、長期 token |
service secrets | runtime | /run/secrets/<name> 唯讀檔案 | 密碼、API key、憑證 | 可公開的完整設定檔 |
service configs | runtime | 預設掛在 /<name> 的檔案 | nginx、feature flag、一般設定 | 需要保密的 credential |
build.secrets | build time | Dockerfile 指令的 temporary mount | 私有 npm registry、SSH、建置 token | runtime 應用程式設定 |
secrets 和 configs 的名稱先在 Compose 頂層宣告,再由每個 service 的同名欄位明確 grant。只宣告 secrets 不會讓所有服務自動看得到它;這個預設阻擋了「新增一個 secret 後整個 Compose project 都能讀」的錯誤假設。
最小的 runtime secrets 與 configs 範例
下面的例子讓 api 同時取得資料庫密碼和一般設定檔:
services: api: image: ghcr.io/example/api:1.0 environment: APP_CONFIG_FILE: /etc/myapp/app.conf DB_PASSWORD_FILE: /run/secrets/db_password secrets: - db_password configs: - source: app_config target: /etc/myapp/app.conf
secrets: db_password: file: ./secrets/db_password.txt
configs: app_config: file: ./config/app.conf啟動後,Compose 只把 db_password 掛到這個 service 的 /run/secrets/db_password;app_config 則掛到指定的 /etc/myapp/app.conf。DB_PASSWORD_FILE 不是 Compose 的特殊語法,它只有在映像內的應用程式或官方映像支援 _FILE 慣例時才會真的讀取該檔案。若程式只接受 DB_PASSWORD,應修改程式的啟動邏輯,而不是把秘密再複製回 environment。
如果不需要自訂路徑,也可以用短語法:
services: api: secrets: - db_password configs: - app_config短語法會使用資源名稱作為容器內的檔名。Linux container 的 config 預設路徑是 /<config-name>;secret 則是 /run/secrets/<secret_name>。需要讓既有程式使用固定路徑時,改用上面的 long syntax 明確寫 target。
secrets 與 configs 真正的差別
Docker 文件把 secret 定義成不應放進 Dockerfile、source code 或未加密儲存的敏感資料;Compose 以檔案方式提供它,並用 service-level grant 限制可見範圍。Config 也會以檔案掛載,但目的只是讓 service 不必重建 image 就能換一般設定,預設檔案權限可能是 world-readable 0444。
因此不要只因為兩者都「掛成檔案」就混用:
app.conf如果包含 host、port、feature flag 或公開的服務設定,可以放configs。db_password.txt、OAuth client secret、TLS private key 或 registry token 應放secrets。- 一份混合檔案同時包含公開設定和秘密時,最好拆成兩個輸入,讓 service 只取得實際需要的部分。
- 需要在 image build 階段登入私有 registry 時,請看 Docker Build secrets 與 ARG、ENV 的差異,不要把 build secret 誤當成啟動後的 runtime secret。
file: 不代表本機 secret 已被加密
本機 Compose 常見的 file: 寫法,實際上仍會由執行 Compose 的使用者讀取 host 檔案。Docker 的 trust model 也提醒,secrets/configs 的 file:、env_file、include 等欄位會讀取 host 可存取的內容,內容還可能在 Compose 載入或 docker compose config 輸出時出現。
這表示要同時做三件事:
- 將
./secrets/放進.gitignore,並用最小的 host 檔案權限限制誰可以讀。 - 不要在公開 CI log 執行會把完整 model 印出來的
docker compose config;要驗證語法時優先用docker compose config -q。 - 在 production 優先使用平台已建立的 external secret/config,或由部署平台在啟動時注入,而不是把正式 secret file 放在 application repository。
external: true 的意思是 Compose 不負責建立資源,會向 container platform 查找既有名稱;找不到就失敗。它能把資源生命週期交給平台,但不等於你已經確認平台的加密、備份、權限與 rotation policy,這些仍要另外驗證。
啟動前做不洩漏內容的驗證
先檢查 Compose model 是否能解析:
docker compose config -q接著在不輸出 secret 內容的前提下,確認路徑與可讀性:
docker compose run --rm api sh -eu -c ' test -r /run/secrets/db_password test -r /etc/myapp/app.conf printf "%s\n" "runtime files are readable"'如果 service 看不到檔案,按這個順序查:
- top-level 名稱是否和 service grant 完全一致。
source是否引用了實際存在的 secret/config。- long syntax 的
target是否和應用程式啟動參數相同。 - container user 是否具備讀取掛載檔案的權限;不要為了快速通過而把整個容器改成 privileged。
- 這份 Compose 是否由另一個
-foverride 合併,意外移除了 secrets 或 configs。
這和 Docker Compose include 拆分設定檔時的路徑問題是同一種排錯習慣:先看 Compose 最終解析出的 model,再看容器內的實際 mount,不要只讀單一 YAML 檔猜測。
結論是:secrets 解決的是 runtime credential 的傳遞邊界,configs 解決的是不必重建 image 的一般設定;兩者都不能代替 secret manager,也不能抵消 host、Compose CLI、容器內程式與 log 的存取風險。先依資料敏感度拆分,再用 explicit grant 和不洩漏內容的檢查命令驗證,才是可維護的 Compose 設計。
常見問題
configs 可以拿來放資料庫密碼嗎?
不建議。Configs 的目的是一般執行期設定,預設掛載檔案權限也可能較寬;密碼、token 與 private key 應使用 secrets,並確認外部平台的保存與輪替方式。
Compose secrets 會自動把 secret 加密嗎?
不能一概而論。Compose 會依 file、environment 或 external 來源取得資料;本機 file: 仍讀取 host 檔案,安全性取決於 host 權限與執行環境。不要把 Compose YAML 裡的 secrets 宣告當成加密保管庫。
為什麼 DB_PASSWORD_FILE 設了卻沒有作用?
_FILE 是許多映像採用的慣例,不是 Compose 自動解析的欄位。先看該映像的文件或 entrypoint 是否會讀取 /run/secrets/db_password;若沒有,就在應用程式啟動邏輯中支援檔案輸入,避免把內容轉回 environment。
參考資料:
Docker Docs:Manage secrets securely in Docker Compose
回報錯字、失效連結,或告訴我你想看的延伸主題。