restart 重啟的是容器裡的行程,不是容器本身。環境變數在建立容器那一刻就固定了,改完 .env 重啟看起來成功,值還是舊的。
發布於 2026-09-06・約 4 分鐘
我把一個服務從模擬環境切到正式環境。改 .env 裡的一行:
.env
SHIOAJI_SIMULATION=0
然後重啟:
docker restart homelab-stock
容器起來了,日誌正常,功能也正常。只是它還在跑模擬環境。
$ docker exec homelab-stock printenv SHIOAJI_SIMULATION 1
檔案裡明明是 0。
docker restart 是「把這個容器裡的主行程停掉再啟動一次」。容器本身沒有被重新建立。
docker restart
環境變數是容器建立時就寫進容器設定的。Compose 讀 env_file、把每一行展開成一組 key/value、塞進容器的 spec 裡,那件事只發生在 docker compose up 建容器的那一刻。之後這份設定就固定在容器上了,.env 檔案再怎麼改都跟這個容器無關——它甚至沒有被掛進去,容器裡根本沒有那個檔案。
env_file
docker compose up
所以 restart 之後的行程,拿到的是建容器當天那份環境變數。
要換掉環境變數只有一條路:重建容器。
docker compose up -d
Compose 會比對現在的設定跟容器上的設定,發現不一樣就砍掉重建。這件事它自己會判斷,你不用加 --force-recreate。
--force-recreate
我後來把規則寫死成三條,因為每次都重新想一遍,每次都會想錯一次:
.py
.js
environment
docker compose build
up -d
第一列要重啟是因為已經載入記憶體的模組不會自己更新。Python 把 import 進來的東西快取在行程裡,檔案改了不會重新讀;Node 也一樣。開發時「我明明存檔了怎麼沒變」十次有八次是這個。
import
第二列就是本文這個坑。
第三列是因為裝在容器裡的東西寫在映像檔層,重建容器不會重跑 Dockerfile。
因為沒有任何一步會失敗。
docker restart 回 0,容器狀態 Up,日誌沒有錯誤,服務對外照常回應。程式讀到的舊值通常是個合法的值(我這個案例是「模擬環境」,一個完全正常的模式),於是它就照舊值好好地跑下去。
Up
如果你改的是一個「開了才會有新行為」的開關,症狀會是「新功能沒出現」——這時你多半會去翻程式碼,懷疑是不是判斷式寫錯,而不是懷疑環境變數沒進去。我在這上面浪費過一個下午。
改任何環境變數之後,第一件事是問容器它現在看到什麼:
docker exec <容器> printenv <變數名>
不要問檔案,檔案永遠是對的。要問容器。
這條在別的地方也救過我。我有一支 API 金鑰換過之後,容器裡的值停在舊的,健康檢查卻一直是綠的——因為那個服務的 /models 端點根本不需要認證,拿舊金鑰去問也會成功。健康檢查通過不代表金鑰是對的,printenv 才是。
/models
printenv
如果變數帶祕密,不要直接印出來。印個布林就好:
docker exec <容器> sh -c 'test -n "$API_KEY" && echo set || echo empty'
或者比對長度、前四碼,總之不要讓完整的金鑰進到終端機的歷史紀錄裡。
docker compose up -d <服務名> 只重建你點名的那一個。如果你的服務之間有依賴(尤其是共用網路命名空間那種),點名重建可能會連帶重建別人,或是漏掉該重建的。不指定服務名讓 Compose 自己算,通常比較安全。
docker compose up -d <服務名>
標籤:Docker、Compose、環境變數、除錯