一個反向代理跑在容器裡,新增了一組 static file 的路徑設定。語法檢查通過,reload 指令也沒報錯,但瀏覽器拿到的是 404。
反覆檢查設定檔,路徑對、格式對、權限對。daemon 狀態正常,其他既有的路由都還在工作。新加的那條路由孤立地失效。
掛載關係停在建立當下
容器啟動的時候,volume 的掛載關係就被讀進去了。啟動之後,在既有的 bind mount 底下新增檔案或資料夾,容器通常看得到,因為那是同一塊實體磁碟。但新增一條掛載定義不一樣:compose 檔改了、加了一個 host 路徑進去,這個新路徑不會因為 daemon reload 就出現在容器視野裡。
daemon reload 會讓容器重讀內部的路由邏輯,但它看到的掛載關係仍然是啟動時那一版。config 裡寫 serve /home/ubuntu/something,如果 /home/ubuntu/something 當初沒被 mount 進來,從容器內部看這個路徑不存在。設定指向一個在容器視野裡不存在的位置。
會被誤判成 config 問題
路由新增沒生效,直覺會查三個地方:語法、reload、權限。這三個都是 config 錯誤的典型根因,mental model 最熟。三個都驗證過還是 404,下一步通常是讀 debug log、看 upstream、甚至重啟整個 daemon,繼續在 config 層打轉。
繞這一圈的原因是,host 檔案系統跟容器掛載邊界的差異平常不會被主動測試。多數時候是改既有 config、或動既有 mount 底下的檔案,這些容器本來就看得到。要新增一條掛載定義的場景一年見不到幾次,邊界也就很難被想起。
一個 ls 就能確認
進容器 docker exec -it <container> ls <path>。
回 No such file or directory,config 怎麼寫都沒用——容器視野裡沒這個目錄。
回得出檔案,那 config 層才值得檢查。
reload 的作用範圍
多數 daemon 的語境裡,reload 指的是「重讀設定並生效」,不中斷服務。nginx reload、systemd reload、postgres reload——都是這個意思。
這些 daemon 多半直接跑在 host 上,沒有容器那層掛載邊界。反向代理跑在容器內的時候,reload 的作用範圍就縮到容器內部——能讀新的 config,但讀不到新增的 mount。不算 reload 失效,是它本來就不經手這層。
下次的排查順序
遇到「config 寫了、daemon 沒報錯、瀏覽器卻 404」的時候,先問這個 host 路徑是不是新加進 compose 的。
是的話,reload 跟 restart 都不會讀到新的 mount。需要回到 compose 那一層,讓容器重新建立,新的掛載定義才會被讀進來。
下次遇到類似現象,先從這裡看起,通常比從 config 一路往下查快。
— 邱柏宇
延伸閱讀
Reload Doesn’t Mean Remount
A reverse proxy, running inside a container, was given a new route: serve static files from this path. Syntax check passed, the reload command produced no errors, and every browser request came back 404.
The config was checked and re-checked. Path correct, format correct, permissions correct. The daemon was healthy, every pre-existing route still worked. The new route failed in isolation.
Mount Relationships Are Set at Creation
When a container starts, its volume mount relationships get read in. After that, adding a new file or folder under an existing bind mount is usually visible from inside — same underlying disk. But adding a new mount definition isn’t the same: if the compose file is edited to include a new host path, that path won’t appear in the container’s view just because the daemon got reloaded.
A daemon reload makes the container reread its internal routing, but the mount relationships it sees are still the ones from startup. A config saying serve /home/ubuntu/something points to something that doesn’t exist in the container’s view, if /home/ubuntu/something wasn’t mounted when the container was created.
Looks Like a Config Problem
When a newly added route fails, the instinct is to check three places: syntax, reload, permissions. Classic config failure modes, the most familiar mental model. When those three pass and the 404 persists, the next steps tend to be debug logs, upstream checks, maybe a full daemon restart — still within the config layer.
The detour happens because the host–container mount boundary rarely gets tested. Most edits touch existing config or files already under existing mounts, which the container can already see. Introducing a new mount definition is a rare scenario, so the boundary isn’t the first thing that comes to mind.
One ls Settles It
docker exec -it <container> ls <path>.
If it returns No such file or directory, no amount of config work helps — the directory isn’t in the container’s view.
If it returns files, then the config layer is worth checking.
The Scope of reload
In most daemon contexts, reload means “reread the config and apply, no downtime.” nginx reload, systemd reload, postgres reload — same idea.
Those daemons usually run on the host, without the container’s mount boundary. When a reverse proxy runs inside a container, reload‘s scope shrinks to the container’s interior — it can read a new config but can’t see a newly added mount. Not a failure of reload; just something outside what it was built for.
Next Time
When the config is in place, the daemon doesn’t complain, and the browser still returns 404 — first question is whether this host path is new to the compose file.
If it is, reload and restart won’t pick up the new mount. Back to the compose layer, so the container can be recreated and the new mount definition read in.
Next time the same symptom shows up, starting here is usually faster than working down from the config.
— Jett
Related Posts
https://justfly.idv.tw/s/wfahii9