dsh 為何顯示 http://127.0.0.1:3080?
dsh 顯示 http://127.0.0.1:3080,是因 Web UI 預設僅繫結 localhost。本文說明 SSH tunnel 的安全連線方式,以及公開 3080 為何會暴露命令執行權限。
dsh web: http://127.0.0.1:3080 的意義
在 VPS 上啟動 DeepSeek Harness web profile 時,程式會列出兩行訊息,然後等待:
dsh web: http://127.0.0.1:3080
Ready.127.0.0.1 是 loopback address。這是電腦用來與自身通訊的位址。繫結至 127.0.0.1 的 socket 只接受同一台電腦上執行的程序連線,其他來源都無法連線。因此,這行訊息同時告訴你兩件事:Web UI 正在何處監聽,以及哪些來源可以連線。只有執行 dsh 的那台電腦可以存取它。
因此,將該 URL 貼到筆記型電腦的瀏覽器時不會有任何反應。筆記型電腦的 127.0.0.1 是筆記型電腦本身。Harness 正在 VPS 的 127.0.0.1 上監聽,而 VPS 是另一台具有不同 loopback stack 的電腦。這不是故障。你需要將連線轉送過去。
官方 README 清楚說明預設值:「此命令會啟動 Web UI,預設由 http://127.0.0.1:3080 提供服務。」繫結位址來自 webserver host plugin @deepseek-ai/dsh-host-webserver;其 host key 的文件說明為「監聽主機;支援的值有 loopback 和 all-interfaces」。除非你主動修改設定,否則預設使用 loopback。如果你不熟悉連接埠,Linux 上的連接埠運作方式說明了這套機制所依據的位址加連接埠模型。
Web UI 僅繫結至 localhost 的原因
dsh 是代理程式控制框架。瀏覽器分頁是操作介面的控制面板,能對您選定的工作區目錄中的程序執行 shell 命令、讀寫檔案,並使用您的模型 API 金鑰。任何能載入該頁面的人,都能以執行 dsh 的使用者身分完成上述操作。
因此,連接埠 3080 並不是唯讀儀表板。載入該頁面即可在伺服器上執行命令。
開啟 Web UI 後,會直接進入工作階段清單。頁面不會顯示登入提示,因為開發者預覽版沒有使用者帳戶,也沒有遠端驗證功能。在 loopback 上,這樣的設計是一致的:作業系統負責存取控制,只有本機程序能夠連線。若在具備公開 IP 的 VPS 上,將同一台伺服器繫結至 0.0.0.0,該頁面就會直接對整個網際網路提供服務,前方沒有任何防護。自動化掃描器會持續掃描不常見的連接埠,因此應將公開的 3080 視為已被發現。
不要在防火牆中開放連接埠 3080,也不要在公開 VPS 上將 Webserverhost設為0.0.0.0。這種組合會把伺服器的命令執行權限交給第一個連線的人。
同樣的原則適用於您放在伺服器上的所有代理程式執行環境。因此,在 VPS 上安全執行 coding agent 也從相同規則開始:代理程式的控制連接埠必須維持私有,並透過您信任的機制連入該連接埠。
如何從筆電開啟 dsh Web UI?
有 3 種合適的方法,而且每種方法都會讓 harness 綁定在 loopback。
- SSH tunnel。不會有任何新的服務監聽 public interface,而且你已經具備所需的 credentials。應使用這個方法。
- Private overlay network。這樣 UI 只能從你自己的裝置連線,其他人都無法看見。
- Terminate TLS(transport layer security)的 reverse proxy。代理轉送任何內容前,先要求 password。
三者的差異在於,哪一種機制會將瀏覽器連線帶到 loopback。任何方法都不應讓 harness 脫離 loopback。
透過 SSH tunnel 存取
請在筆記型電腦上執行以下指令,不要在 VPS 上執行:
ssh -N -L 3080:127.0.0.1:3080 you@your-vps讓指令持續執行,然後在本機瀏覽器開啟 http://127.0.0.1:3080。Web UI 會載入。
-L 引數包含以冒號分隔的 3 個欄位。第一個欄位是要在筆記型電腦上開啟的連接埠。第二和第三個欄位是每個連線要轉送到的位址與連接埠。關鍵在於:中間欄位中的 127.0.0.1 是由 VPS 上的 SSH server 解析,而且是在網路流量抵達 VPS 之後才進行解析。它代表的是 VPS 的 loopback,而不是筆記型電腦的 loopback。這正是 dsh 顯示的位址,因此即使直接用瀏覽器連線失敗,tunnel 仍能正常運作。
-N 會告訴 SSH 不要執行遠端命令,因此只建立轉送器,不提供 shell。若要建立背景 tunnel,並在失敗時明確報錯而不是靜默失敗:
ssh -N -f -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -L 3080:127.0.0.1:3080 you@your-vps-f 會在驗證完成後將 SSH 放到背景執行。ExitOnForwardFailure=yes 的重要性比表面上更高:沒有這個選項時,即使轉送未能建立,SSH 仍會成功連線,因此你會得到正常運作的 session,卻得到沒有任何警告的失效 tunnel。ServerAliveInterval=30 每 30 秒傳送一次 keepalive,讓閒置的 tunnel 能在咖啡廳與飯店路由器的 NAT(network address translation)逾時後繼續運作。
你應該看到的內容
在 VPS 上確認實際正在監聽的內容:
ss -ltnp | grep 3080正常結果會列出 loopback 位址:
LISTEN 0 511 127.0.0.1:3080 0.0.0.0:* users:(("node",pid=1042,fd=21))如果本機位址欄顯示 0.0.0.0:3080,表示 Web UI 正在所有介面上監聽,包括公開介面。請先停止它並修正 bind 設定,再進行其他操作。如果 ss 顯示 socket,卻讓 users: 欄位留白,請搭配 sudo 執行,否則由其他使用者擁有的 socket 會隱藏程序名稱。
tunnel 無法啟動時
SSH 會顯示以下訊息並結束:
bind [127.0.0.1]:3080: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 3080這是筆記型電腦端的問題,不是 server 的問題。本機可能已有其他程序占用 3080 連接埠,常見原因是先前忘記停止的 tunnel。請改用未占用的本機連接埠:
ssh -N -L 3081:127.0.0.1:3080 you@your-vps只有第一個欄位變更,因此現在要瀏覽 http://127.0.0.1:3081,而 harness 仍持續監聽 3080。兩個數字不需要相同。
如果 tunnel 已建立,但瀏覽器回報連線遭拒或收到空白回應,表示網路流量已轉送到 VPS,卻在遠端找不到任何監聽中的服務。可能是 dsh 已結束,也可能是它繫結到其他連接埠。請在 server 上使用 ss -ltnp | grep 3080 檢查。
這裡還有一個常見問題。前景執行的 npx @deepseek-ai/dsh web 會在 shell 關閉時結束,因此你登出後 harness 也會立即停止。請在 tmux 中啟動它,或使用 systemd user service;這與 讓 coding agent 持續在 VPS 上執行 是同一類問題。在處理 SSH 端設定時,建議先完成 強化 VPS 上的 SSH,因為這個 tunnel 會讓 SSH 登入成為連線至 agent 的唯一入口。
透過私有 overlay network 存取
overlay network 會為 VPS 與筆記型電腦提供私有網路位址,只有加入該網路的裝置能夠連線。Tailscale 是常見選擇,其 serve command 正好適用於此情境:tailscaled 會在 VPS 上執行,並連線至 localhost:3080 本身,因此 harness 仍維持在 loopback 上,無須變更 dsh 的設定方式。
tailscale serve --bg localhost:3080
tailscale serve status之後即可透過 tailnet 內該機器的名稱,以 HTTPS 存取 UI,且不必在公開介面開放連接埠。這需要為 tailnet 啟用 HTTPS 憑證,否則 serve 沒有憑證可提供。若要停止服務,請使用 off 重複執行該 command:
tailscale serve --https=443 off請使用 serve,不要使用 funnel。Funnel 會將相同的目標公開至公用網際網路,讓你回到在開放連接埠上執行未經驗證的 agent runtime。這兩個 command 幾乎相同,但作用完全相反,因此請先閱讀Tailscale Serve 與 Funnel 的差異,再執行其中一個。Tailscale 作為私有網路涵蓋設定本身。
透過要求密碼的反向代理存取
這是確實會將連接埠公開至網際網路的選項,因此驗證是陌生人與伺服器命令執行權限之間的唯一防線。當多人需要使用 UI,而各自建立 tunnel 不切實際時,請選擇此方式。
harness 維持在 127.0.0.1:3080。nginx 執行於同一台主機,因此可以連線至 loopback;它會在 443 上監聽,並使用憑證與密碼檔案。
server {
listen 443 ssl;
server_name dsh.example.com;
ssl_certificate /etc/letsencrypt/live/dsh.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dsh.example.com/privkey.pem;
auth_basic "dsh";
auth_basic_user_file /etc/nginx/dsh.htpasswd;
location / {
proxy_pass http://127.0.0.1:3080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s;
proxy_buffering off;
}
}建立密碼檔案並重新載入:
sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/dsh.htpasswd you
sudo nginx -t && sudo systemctl reload nginxnginx -t 應輸出 syntax is ok,接著輸出 test is successful。檔案內容錯誤時,重新載入會失敗,但執行中的設定仍會保留。因此,請先讀取錯誤訊息,不要直接盲目重新啟動。
代理設定中的其中 3 行並非裝飾。Upgrade 與 Connection 標頭可讓 WebSocket handshake 通過。缺少這些標頭時,頁面雖然會載入,但不會更新。proxy_read_timeout 3600s 取代預設的 60 秒逾時。否則長時間的 agent 執行會在回應中途被中斷,讓 UI 看起來像是當機。proxy_buffering off 會在模型輸出抵達時立即傳送至瀏覽器,而不是等到回應完成後才一次送出。逐行說明 nginx 反向代理設定介紹其餘內容;在 nginx、Caddy 與 Traefik 之間選擇則說明如何使用自動憑證完成相同設定。
無論選擇哪個代理,都應在防火牆上關閉 3080,讓唯一的進入路徑是經過驗證的代理。ufw 防火牆基礎說明相關規則。透過 TLS 使用基本驗證只是最低限度的防護,不是完整的安全模型:持有該密碼的人,就持有伺服器上的 shell 存取權限。可以使用 tunnel 時,請優先採用 tunnel。
如何變更 dsh Web 服務監聽的連接埠?
--port 屬於 Web 應用程式,而不是啟動器。CLI 文件直接提供了範例:
dsh --profile web --port 8080dsh web 是 --profile web 的別名,因此 dsh web --port 8080 是相同的命令。啟動器只解析自己的旗標,並將其後的所有內容傳給啟動的設定檔。因此,啟動器旗標必須放在前面,而啟動器無法辨識的第一個 token 會開始作為應用程式引數。請將 --port 放在設定檔之後,切勿放在設定檔之前。
請讀取命令輸出的 URL,不要自行假設,因為該行會顯示伺服器實際繫結的位址。接著更新 tunnel 的最後一個欄位,使其相符:
ssh -N -L 3080:127.0.0.1:8080 you@your-vps若要永久變更,連接埠應設定在設定檔組態中,而不是命令列上。web 和 headless 設定檔會在首次使用時,從 ~/.dsh 下隨附的範本自動初始化。您的 API key 和模型端點設定也位於同一個目錄,因此如果您本來就要編輯這些檔案,設定 dsh keys、models 與 endpoints 是應一併閱讀的相關說明。若要查看所有層級組合後實際生效的設定:
dsh --dump-configWebserver plugin 只公開兩個 key:host 和 port。將 port 設為 0 會要求作業系統指派可用的連接埠,文件說明為「zero requests an OS-assigned port」。這可確保不會發生連接埠衝突,但不適合 tunnel,因為每次重新啟動時連接埠號碼都會變更。
為什麼 dsh 會因位址已在使用中而失敗?
因為另一個程序已經占用該位址與連接埠,核心會拒絕第二次繫結。Node 會顯示如下錯誤:
Error: listen EADDRINUSE: address already in use 127.0.0.1:3080先找出占用者,再進行變更:
sudo ss -ltnp | grep 3080users:(("node",pid=1042,fd=21)) 欄位會列出程序及其 PID。通常是較早啟動、但你以為已停止的 dsh,而且它可能仍在分離的 tmux 視窗中執行。使用 kill 1042 停止該程序,或讓新的執行個體使用其他連接埠。請注意,127.0.0.1:3080 與 0.0.0.0:3080 也會互相衝突,因為繫結所有介面時已經包含 loopback。
固定版本,因為這是開發者預覽版
README 已明確說明:DeepSeek Harness 目前處於開發者預覽階段,仍在快速迭代,未來可能會有造成相容性中斷的變更。
npx @deepseek-ai/dsh web 每次執行時都會解析為最新發布版本。伺服器若一週未曾操作,下次啟動時可能載入不同的 CLI,且使用不同的旗標。請固定版本,避免重新啟動時同時升級:
npx @deepseek-ai/dsh@0.1.0-rc.7 web截至 August 2026,已發布的套件版本為 0.1.0-rc.7。接受前,先確認未指定版本的 npx 會取得什麼內容:
npm view @deepseek-ai/dsh version如果固定版本後無法安裝,或固定新版後 npx 仍持續啟動舊版本,請參閱這項操作引發的安裝與版本錯誤,其中說明如何清除 npx 快取,以及確認 Node 內含的 npm 實際指向哪個版本。
在預覽版本之間,旗標可能會在啟動器與 Web 應用程式之間移動。如果 --port 的行為不再符合本指南的說明,請向應用程式查詢其自身的旗標清單,不要自行猜測:
dsh --profile web --help如需安裝本身、workspace 設定與模型金鑰的說明,請參閱在 VPS 上安裝 DeepSeek Harness。如果只需要較精簡的存取步驟說明,在 VPS 上存取 dsh Web UI 會說明不含推理功能的 tunnel。
FAQ
為什麼我在筆記型電腦的瀏覽器中無法開啟 http://127.0.0.1:3080?
因為 127.0.0.1 代表您輸入指令的那台機器。DeepSeek Harness Web UI 綁定 VPS 的 loopback 位址,因此只有 VPS 上的程序能連線。您的筆記型電腦有獨立的 loopback,而且該機器的 port 3080 沒有程序監聽。使用 ssh -N -L 3080:127.0.0.1:3080 you@your-vps 透過 SSH 轉送 port,然後在本機載入 http://127.0.0.1:3080。-L 引數的中間欄位會在伺服器端解析,因此會指向 harness。
將 dsh Web UI 綁定至公用 VPS 的 0.0.0.0 是否安全?
不安全。Web UI 是代理程式的控制介面。代理程式會以執行 dsh 的使用者身分執行 shell 命令並編輯檔案,而且開發者預覽版完全沒有登入畫面。綁定至公用 IP 的所有介面後,任何能連到 port 3080 的人都能在伺服器上執行命令。請維持綁定至 127.0.0.1,在防火牆上關閉 3080,並使用 SSH tunnel、私有 overlay network,或要求密碼的反向代理。
關閉 SSH 工作階段後,如何讓 dsh Web UI 持續執行?
前景執行的 npx @deepseek-ai/dsh web 是登入 shell 的子程序,因此 shell 結束時也會被終止。請在 tmux 工作階段中啟動它,並使用 Ctrl-b d 脫離;或者啟用 lingering,將它作為 systemd user service 執行。tunnel 與 harness 彼此獨立:只要 harness 本身有一個生命週期超過登入 shell 的父程序,您就能任意中斷並重新建立 SSH tunnel,而不會影響正在執行的 harness。
為什麼 dsh Web UI 透過 nginx 執行長時間代理程式工作時,會在中途停止回應?
因為 nginx 的預設 proxy_read_timeout 為 60 秒。連線若持續 1 分鐘沒有資料,nginx 就會關閉連線,而長時間的代理程式步驟很容易出現這種情況。請在 location 區塊中設定 proxy_read_timeout 3600s;。加入 proxy_buffering off;,讓輸出在產生後立即串流至瀏覽器;並搭配 proxy_http_version 1.1; 傳送 Upgrade 與 Connection 標頭,讓 WebSocket handshake 成功。缺少這些標頭時,頁面雖然會載入,但永遠收不到更新。