一、為什麼 Mac 本機有 Clash,容器裡卻像「沒開」
Docker Desktop for Mac 會在輕量虛擬化環境裡跑 Linux 引擎;從容器視角來看,你的應用程式跑在一個與 macOS 使用者空間隔離的網路堆疊上。瀏覽器與多數 macOS 應用程式遵循系統的 Proxy 設定或自行走本機 Socket,但容器內的行程預設不會繼承你在終端機裡設定的代理,除非你透過環境變數、Compose、或映像內設定主動注入。
另一個常見誤區是:既然 Clash 聽在 127.0.0.1:7890,在容器裡也填同一組位址就好?在預設橋接網路下,127.0.0.1 幾乎永遠指向容器自己的回環介面,上面並沒有你的 Clash 行程,因此會出現連線被拒或逾時。正確做法是讓流量指向可解析到 Mac 主機的名稱或 IP;在 Docker Desktop 上,官方提供的慣用入口就是 host.docker.internal(語意上代表「宿主機」)。下文會以混合埠為例示範,實際埠號請在客戶端介面核對。
第三個層次是誰在發起連線:docker pull 與背景映像同步,多數情況由 Docker 守護行程處理,而不是你當前 shell;僅在互動式容器裡 export http_proxy,並不會自動讓守護行程跟著走代理。因此要分開處理「容器執行期出網」與「主機側拉映像」兩條鏈路。
二、先決條件:本機 Clash 要能被「區網路徑」連到
容器經由 host.docker.internal 連回 Mac 時,對 Clash 來說等同於來自虛擬區網的連入。若你的混合埠只綁在 127.0.0.1,外層命名空間無法連線,容器自然也會失敗。實務上請在設定中啟用允許區域網路(allow-lan: true),並確認監聽位址與埠號與客戶端顯示一致;細節可參考《Clash 開啟區域網路代理》,概念與「手機連筆電代理」相同,只是把 Docker 視為另一個需要連到你混合埠的客戶端。
若你尚未完成訂閱匯入與基礎啟動流程,可先依《Clash Verge Rev 設定教學》把本機規則跑通,再回來做容器側設定,除錯時較容易分辨是「代理沒開」還是「容器路徑不通」。
下文範例以常見的 mixed-port 7890 表示 HTTP 與 SOCKS 匯出的混合埠;若你的環境使用不同埠,請全文替換。少數工具偏好 socks5:// 前綴,屆時把 URL 改成對應協定即可,核心思路不變。
三、host.docker.internal:從容器指回 Mac 主機
在 Docker Desktop macOS 的預設設定下,host.docker.internal 會解析到一個可從容器路由到 Mac 的位址,讓你在容器內用形如 http://host.docker.internal:7890 的代理 URL 連到本機 Clash。相較於手寫每次可能變動的閘道 IP,這個主機名稱在團隊文件與 Compose 檔案中更好維護。
建議先用互動式容器驗證名稱與埠是否可達(映像可改用你手邊已有的 Debian/Ubuntu):
docker run --rm -it debian:stable-slim bash
apt-get update -qq && apt-get install -y -qq curl
curl -I --proxy "http://host.docker.internal:7890" https://www.google.com
若第二行能回傳 HTTP 標頭,代表「容器 → Mac 主機混合埠」這段已打通;若此處失敗,請先回到上一節檢查監聽與 allow-lan,不要急著改規則檔。部分環境亦提供 gateway.docker.internal 作為輔助,實務上以 Docker 官方文件與你安裝的版本為準;一般教學與社群範例仍以 host.docker.internal 為主。
四、docker run:用環境變數注入 HTTP_PROXY
確認連線沒問題後,可把代理寫進環境變數,讓 apt、curl、wget 等遵循標準變數的工具自動走 Clash:
docker run --rm -it \
-e http_proxy="http://host.docker.internal:7890" \
-e https_proxy="http://host.docker.internal:7890" \
-e HTTP_PROXY="http://host.docker.internal:7890" \
-e HTTPS_PROXY="http://host.docker.internal:7890" \
-e NO_PROXY="localhost,127.0.0.1,::1" \
debian:stable-slim bash
說明:許多程式會讀小寫 http_proxy,也有程式只認大寫 HTTP_PROXY,兩組並列最省事。NO_PROXY 用於排除本機服務,避免流量繞一圈又回到代理。若你還會在容器內連到同橋接網路上的其他容器名稱,可視需要把該主機名稱一併加入排除清單。
對 Git 與 npm,除環境變數外仍可沿用專案慣用的 git config 與 npm config set https-proxy;在 CI 映像與本機除錯容器之間切換時,統一走 host.docker.internal 可減少「在我電腦上可以」的落差。
五、Docker Compose:服務層級固定容器代理
實際專案很少只用單次 docker run,多半會用 Compose 管理多個服務。可在 docker-compose.yml 使用 environment 區塊或共用的 env_file,讓每個需要出網的服務帶上同一組代理字串:
services:
web:
image: node:20-bookworm
environment:
http_proxy: http://host.docker.internal:7890
https_proxy: http://host.docker.internal:7890
HTTP_PROXY: http://host.docker.internal:7890
HTTPS_PROXY: http://host.docker.internal:7890
NO_PROXY: localhost,127.0.0.1,::1
若只有建置階段需要代理(例如 npm ci),也可把變數寫在 build.args 裡,並在 Dockerfile 以 ARG 接收,避免執行期映像仍帶著公司內網代理字串。多階段建置時,常見作法是在 builder 階段設定 ENV,最終 runtime 階段不再繼承敏感或環境相依的 URL。
docker build 時,BuildKit 會傳遞 HTTP_PROXY 等建置參數;你也可以在命令列加上 --build-arg HTTPS_PROXY=http://host.docker.internal:7890 做一次性測試。若建置過程仍連不出去,請分清楚是 Dockerfile 內某個步驟沒讀到變數,還是守護行程層仍被公司網路或 VPN 攔截。
六、守護行程與映像拉取:Docker Desktop 內建代理設定
當 docker pull 在 Mac 上失敗時,問題經常出在守護行程沒有代理,而不是容器內。請打開 Docker Desktop 設定,尋找與 Proxies/Resources 相關的頁面(介面隨版本略有差異),在系統或手動代理欄位填入可讓Mac 本機行程連到 Clash 的位址。
實務上,守護行程跑在 Mac 側,使用 http://127.0.0.1:7890 往往可行,因為請求發起點與 Clash 位於同一套使用者空間網路堆疊;這與容器內必須用 host.docker.internal 的情況不同,請勿混用。若你使用需要認證的上游公司代理,則依 Docker 文件填入完整 URL;純本機 Clash 混合埠通常不需要帳密。
完成設定後重啟 Docker 或套用變更,再執行 docker pull hello-world 觀察是否仍逾時。若仍失敗,可對照《Clash 常見報錯與排除》檢查 DNS、規則命中與日誌,確認是不是特定網域被規則丟到錯誤策略。
七、容器內套件管理器:apt、npm、pip 的共通點
在已注入 HTTP_PROXY 的前提下,多數 CLI 工具會自動跟著走。Debian/Ubuntu 的 apt 另可寫入 /etc/apt/apt.conf.d/ 下的 Proxy 設定檔,行為與在實體機上相同;若你建的是長期開發容器,可將該檔放在映像層或掛載 volume,避免每次進容器都要重設。
npm、yarn、pnpm 通常尊重環境變數;若遇特例,再用各工具專屬的 proxy 設定鍵補強。pip 可使用 pip install --proxy 或依環境變數;關鍵仍是「容器內看到的代理主機必須是 host.docker.internal(或等效路由),而不是 127.0.0.1」。
若代理已通但出現名稱解析錯誤,請同步檢查 Clash 的 DNS 模式與 fake-ip 設定;容器內的 /etc/resolv.conf 由 Docker 管理,與你 Mac 上的解析路徑未必完全一致,必要時以實際 dig 或 nslookup 結果對照日誌排查。
八、和 WSL2 路線差在哪裡
Windows 上的 WSL2 與本機通訊時,常要查預設閘道 IP或處理防火牆輸入規則;macOS 上的 Docker Desktop 則習慣直接用 host.docker.internal 當穩定別名。兩者都遵循同一個大原則:代理埠必須對「從子系統/容器來的那條路徑」可達,且守護行程與容器執行期要分開設定。
若團隊同時有 Mac 與 Windows 開發者,建議在 README 分欄維護兩組片段,或把代理 URL 抽成環境變數由每位開發者在 .env 本機覆寫,避免把 Windows 閘道 IP 硬寫進共用 Compose 檔。完整 WSL2 步驟仍以該篇教學為準。
九、除錯清單:仍連不上時依序核對
- 先用 curl 測 host.docker.internal:互動容器內帶
--proxy測試,區分是「到不了混合埠」還是「特定網域規則問題」。 - 混合埠是否為 7890:客戶端若改埠,Compose 與 Desktop 代理欄位要一併更新。
- allow-lan 與繫結位址:僅聽 127.0.0.1 時,容器路徑通常會失敗。
- docker pull 與 docker run 分開看:前者檢查 Docker Desktop 守護行程代理;後者檢查容器環境變數。
- VPN 與公司網路:部分全隧道 VPN 會改寫路由,使虛擬區網路徑暫時不可達,可短暫關閉對照測試。
- 規則與 DNS:代理 TCP 已通但 HTTPS 仍失敗時,回到 Clash 日誌看是否命中 DIRECT 或 DNS 異常。
十、小結
Docker Desktop macOS 與本機 Clash 串接的關鍵,是接受「容器裡的 127.0.0.1 不是 Mac」這個前提,改以 host.docker.internal 搭配正確埠號,並用 HTTP_PROXY/HTTPS_PROXY 與 NO_PROXY 覆蓋執行期工具;映像拉取則別忘了在 Docker Desktop 內為守護行程單獨設定可走本機混合埠的代理。把這兩條鏈路分開思考後,多數「主機能上、容器不能」的問題都能快速定位。
相較於在每台容器內再跑一份核心,共用 Mac 上已調好的訂閱與分流,通常更省事、規則也更一致;前提是你願意把 allow-lan、Compose 與 Desktop 代理當成專案基礎建設的一部分寫進文件。若你希望用圖形介面統一管理混合埠、區域網路與日誌,減少手動改 YAML 的頻率,可優先選擇持續維護的桌面客戶端體驗。→ 立即免費下載 Clash,開啟流暢上網新體驗