dsh web 的 http://127.0.0.1:3080 是什麼?
dsh 顯示 http://127.0.0.1:3080,是因為 Web UI 僅繫結 localhost。了解 SSH tunnel 的安全連線方式,以及直接公開 port 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 位址,也就是機器用來與自身通訊的位址。繫結至 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 金鑰的文件說明為「監聽主機;支援的值有 loopback 和 all-interfaces」。除非你主動修改設定,否則預設會使用 loopback。如果你不熟悉連接埠,請參閱 Linux 上的連接埠運作方式,了解這裡所依賴的位址加連接埠模型。
Web UI 僅繫結至 localhost 的原因
dsh 是代理程式執行框架,也就是包裝在模型外部的程式:它負責控制迴圈、工具呼叫,以及這些呼叫所使用的權限。瀏覽器分頁是控制介面,可操作執行 shell 命令、讀寫所選工作區目錄中的檔案,並使用模型 API key 的程序。任何能載入該頁面的人,都能以執行 dsh 的使用者身分完成這些操作。能夠取得這些權限的途徑也不只有網路:你安裝的 plugin 會在相同程序中,以相同權限執行。因此,安裝 dsh plugin 前先進行審查,重要性不亞於決定伺服器要監聽哪些項目。
因此,port 3080 不是唯讀儀表板。載入該頁面即可在伺服器上執行命令。
開啟 Web UI 後,會直接進入工作階段清單。畫面不會要求登入,因為 developer preview 不提供使用者帳號,也不提供遠端驗證。在 loopback 上,這樣的設計是合理的:作業系統負責存取控制,只有本機程序能夠連線。若在具有公開 IP 的 VPS 上,將同一部伺服器繫結至 0.0.0.0,該頁面就會直接回應整個網際網路,前方沒有任何防護。自動化掃描器會持續掃描不常見的 port,因此應將公開的 3080 視為已被發現。
不要在防火牆中開放 port 3080,也不要在公開的 VPS 上將 webserverhost設為0.0.0.0。這種組合會讓第一個連線的人取得伺服器上的命令執行權限。
相同原則適用於你部署在伺服器上的所有代理程式執行環境。因此,在 VPS 上安全執行 coding agent 也應從相同規則開始:代理程式的控制 port 必須保持私有,並透過你信任的方式連線至該服務。
如何從筆電開啟 dsh Web UI?
有 3 種合理方式,而且每一種都會讓 harness 綁定在 loopback。
- SSH tunnel。不會有任何新的服務在 public interface 上監聽,而且你已經具備認證資訊。應使用這種方式。
- Private overlay network,讓 UI 可從你自己的裝置連線,其他人則無法看見。
- Terminate TLS(transport layer security)的 reverse proxy,在轉送任何內容前要求密碼。
三者的差異在於,瀏覽器透過何種方式連到 loopback。這些方式都不應讓 harness 脫離 loopback。
透過 SSH tunnel 存取
請在 laptop 上執行,不要在 VPS 上執行:
ssh -N -L 3080:127.0.0.1:3080 you@your-vps讓它持續執行,然後在本機瀏覽器開啟 http://127.0.0.1:3080。Web UI 會載入。
-L 引數包含 3 個以冒號分隔的欄位。第 1 個欄位是要在 laptop 上開啟的埠。第 2、3 個欄位是要將每個連線轉送至的位址與埠。關鍵在於:中間欄位的 127.0.0.1 是由 VPS 上的 SSH server 解析,且發生在網路流量抵達 VPS 之後。它代表的是 VPS 的 loopback,而不是你的 laptop。這正是 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 會在驗證完成後將程序放入背景。ExitOnForwardFailure=yes 比看起來更重要:沒有它時,即使轉送無法建立,SSH 仍會成功連線,因此你會得到正常的工作階段,但 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這是 laptop 的問題,不是 server 的問題。本機已有其他程序占用 3080 埠,通常是先前忘記關閉的 tunnel。請改用可用的本機埠:
ssh -N -L 3081:127.0.0.1:3080 you@your-vps只有第 1 個欄位變更,因此現在請瀏覽 http://127.0.0.1:3081,而 harness 仍會在 3080 上監聽。這 2 個數字不必相同。
如果 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 指令正好適用於此情境:tailscaled 會在 VPS 上執行並連線至 localhost:3080 本身,因此 harness 會維持監聽 loopback,無須變更 dsh 的設定方式。
tailscale serve --bg localhost:3080
tailscale serve status接著,您可以透過 tailnet 內的電腦名稱,使用 HTTPS 存取 UI,且公用介面不需要開放任何連接埠。這需要為 tailnet 啟用 HTTPS 憑證,否則 serve 沒有可提供的憑證。若要停止此服務,請將 off 再次搭配該指令執行:
tailscale serve --https=443 off請使用 serve,不要使用 funnel。Funnel 會將相同的目標發布至公用網際網路,使您重新回到在開放連接埠上執行、未經驗證的 agent runtime。這兩個指令看起來幾乎相同,但作用完全相反,因此請先閱讀 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 的金鑰、模型與端點。若要查看所有層級組合後實際生效的設定:
dsh --dump-configWebserver plugin 只提供兩個金鑰: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 目前處於開發者預覽階段,仍在快速迭代,未來會有造成相容性問題的變更。如果你因為這種變更速度而猶豫,dsh 與 Claude Code 及 Omnigent 的比較會將它與處於同一發展曲線、但階段不同的兩個 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如需安裝、工作區設定與模型金鑰,請參閱在 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,且該主機的 3080 埠沒有程序監聽。使用 ssh -N -L 3080:127.0.0.1:3080 you@your-vps 透過 SSH 轉送連接埠,然後在本機載入 http://127.0.0.1:3080。-L 引數的中間欄位會在伺服器端解析,因此它才能指向 harness。
將 dsh Web UI 綁定至公用 VPS 上的 0.0.0.0 是否安全?
不安全。Web UI 是代理程式的控制介面。代理程式會以執行 dsh 的使用者身分執行 shell 命令並編輯檔案,而且開發者預覽版完全沒有登入畫面。綁定至公用 IP 上的所有介面,表示任何能連到 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 本身有一個生命週期超過登入工作階段的父程序,您可以任意中斷並重新建立 SSH tunnel,而不會影響正在執行的 harness。
為什麼 dsh Web UI 透過 nginx 執行長時間代理工作時,會在中途停止回應?
因為 nginx 的預設 proxy_read_timeout 為 60 秒。連線若持續一分鐘沒有資料,nginx 就會關閉該連線,而長時間的代理步驟很容易發生這種情況。請在 location 區塊中設定 proxy_read_timeout 3600s;。加入 proxy_buffering off;,讓輸出一產生就串流至瀏覽器;並透過 proxy_http_version 1.1; 傳送 Upgrade 與 Connection 標頭,讓 WebSocket handshake 成功。缺少這些標頭時,頁面雖然會載入,但永遠收不到更新。