dsh 为什么显示 http://127.0.0.1:3080?
dsh 输出 http://127.0.0.1:3080 是因为 Web UI 仅绑定 localhost。本文介绍 SSH 隧道的安全访问方法,并解释为何公开 3080 端口不安全。
dsh web: http://127.0.0.1:3080 的含义
在 VPS 上启动 DeepSeek Harness Web 配置后,它会输出两行内容,然后等待:
dsh web: http://127.0.0.1:3080
Ready.127.0.0.1 是回环地址。计算机使用它与自身通信。绑定到 127.0.0.1 的套接字只接受来自同一台计算机上进程的连接,其他来源的连接都会被拒绝。因此,这一行同时说明了两点:Web UI 监听的地址,以及允许访问它的来源。只有运行 dsh 的计算机可以访问它。
因此,将该 URL 粘贴到笔记本电脑的浏览器中不会有任何反应。笔记本电脑的 127.0.0.1 指向笔记本电脑自身。Harness 监听的是 VPS 的 127.0.0.1,而 VPS 是另一台计算机,使用不同的回环网络栈。没有任何组件损坏。您需要通过转发连接来访问它。
官方 README 明确说明了默认设置:“该命令会启动 Web UI,默认通过 http://127.0.0.1:3080 提供服务。”绑定地址来自 Web 服务器主机插件 @deepseek-ai/dsh-host-webserver,其 host 键的文档说明为:“监听主机;支持的值有 loopback 和 all-interfaces。”除非您主动修改设置,否则默认使用 loopback。如果您不熟悉端口,请参阅Linux 中的端口工作方式,其中介绍了这里所依赖的地址加端口模型。
Web UI 为什么仅绑定到 localhost
dsh 是一个代理运行框架,即包裹模型运行的程序:它负责运行循环、调用工具,以及管理这些调用所使用的权限。浏览器标签页是一个进程的控制界面,而该进程可以执行 shell 命令,读写您所选工作区目录中的文件,并消耗您的模型 API 密钥。任何能够加载此页面的人,都可以以运行 dsh 的用户身份执行这些操作。进入这些权限范围的不只有网络:您安装的插件也会在同一进程中以相同权限运行。因此,安装 dsh 插件前进行审核,应像决定服务器监听的地址和端口一样谨慎。
因此,端口 3080 不是只读仪表板。加载该页面即可在服务器上执行命令。
打开 Web UI 后,您会直接进入会话列表。页面不会提示登录,因为开发者预览版不提供用户账户和远程身份验证。在回环地址上,这种设计是一致的:操作系统负责访问控制,只有本地进程可以连接。将同一服务器绑定到 VPS 上的 0.0.0.0,而该 VPS 具有公网 IP 后,这个页面就会直接响应整个互联网,前面没有任何防护层。自动扫描器会持续扫描不常见的端口,因此应将公开的 3080 视为已经被发现。
不要在防火墙中开放端口 3080,也不要在公网 VPS 上将 Web 服务器的host设置为0.0.0.0。这种组合会把服务器上的命令执行权限交给第一个连接的人。
同样的原则适用于您部署在服务器上的所有代理运行时。因此,在 VPS 上安全运行编码代理也应遵循同一条规则:代理的控制端口必须保持私有,并由您信任的组件负责连接到该端口。
如何从笔记本电脑打开 dsh Web UI?
有3种可行方式,而且每种方式都会让 harness 继续绑定到 loopback。
- SSH 隧道。不需要在公网接口上新增监听端口,并且您已经具备凭据。应使用这种方式。
- 私有覆盖网络。这样只有您自己的设备可以访问 UI,其他人无法看到。
- 终止 TLS(传输层安全)的反向代理。代理转发任何请求前都要求输入密码。
它们的区别在于,浏览器通过什么方式访问 loopback。无论使用哪种方式,都不应让 harness 脱离 loopback。
通过 SSH 隧道访问
在您的笔记本电脑上运行此命令,不要在 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 服务器解析,此时网络流量已经到达 VPS。它表示 VPS 的回环地址,而不是您笔记本电脑的回环地址。这正是 dsh 输出的地址,因此直接在浏览器中连接失败时,隧道仍然可以正常工作。
-N 告诉 SSH 不要运行远程命令,因此只建立转发,不启动 shell。若要让后台隧道在失败时明确报错,而不是静默失败:
ssh -N -f -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -L 3080:127.0.0.1:3080 you@your-vps-f 会在完成身份验证后将 SSH 放入后台。ExitOnForwardFailure=yes 比看起来更重要:没有它时,即使转发无法建立,SSH 仍会成功连接。这样您会得到一个正常的 SSH 会话,但隧道已经失效,且不会收到警告。ServerAliveInterval=30 每 30 秒发送一次保活消息,使空闲隧道能够避免咖啡馆和酒店路由器上的 NAT(网络地址转换)超时。
您应看到的内容
在 VPS 上确认实际处于监听状态的内容:
ss -ltnp | grep 3080正常结果会显示回环地址:
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 监听所有接口,包括公网接口。先停止它并修正绑定地址,再进行其他操作。如果 ss 输出了套接字,但 users: 字段为空,请使用 sudo 运行,因为当套接字由其他用户拥有时,进程名称默认会被隐藏。
隧道无法启动时
SSH 会输出以下内容,然后退出:
bind [127.0.0.1]:3080: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 3080这是您笔记本电脑上的问题,不是服务器上的问题。本地已经有其他程序占用了 3080 端口,通常是之前忘记关闭的隧道。请改用一个空闲的本地端口:
ssh -N -L 3081:127.0.0.1:3080 you@your-vps这里只修改了第一个字段,因此现在应浏览 http://127.0.0.1:3081,而 harness 仍在 3080 端口上监听。这两个数字不必相同。
如果隧道已建立,但浏览器报告连接被拒绝或收到空响应,说明网络流量已经到达 VPS,但远端没有任何程序在相应位置监听。可能是 dsh 已退出,也可能是它绑定到了其他端口。在服务器上使用 ss -ltnp | grep 3080 检查。
这里还有一个容易忽略的问题。前台运行的 npx @deepseek-ai/dsh web 会在 shell 关闭时退出,因此您注销后 harness 会立即停止。请在 tmux 中启动它,或将其配置为 systemd 用户服务。这与 让 coding agent 在 VPS 上持续运行 是同一个问题。在处理 SSH 配置时,建议先加固 VPS 上的 SSH,因为该隧道使您的 SSH 登录成为访问 agent 的唯一入口。
通过私有覆盖网络访问
覆盖网络会为您的 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 环境中。这两个命令几乎完全相同,但作用相反,因此在输入任一命令前,请先阅读Tailscale Serve 与 Funnel 的区别。将 Tailscale 用作私有网络介绍具体设置过程。
通过需要密码验证的反向代理访问
这是确实会将端口发布到互联网的方案。因此,身份验证是陌生人与服务器上的命令执行之间唯一的防线。当多人需要使用 UI,且无法为每个人单独建立隧道时,可以选择此方案。
harness 继续监听 127.0.0.1:3080。nginx 运行在同一台主机上,因此可以访问回环地址,并使用证书和密码文件监听 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 握手通过。缺少这两个请求头时,页面可以加载,但不会更新。proxy_read_timeout 3600s 会替换 60 秒的默认值。否则,长时间运行的 agent 会在响应过程中被中断,UI 看起来像是卡住了。proxy_buffering off 会在模型输出到达时立即发送到浏览器,而不是等响应完成后一次性发送。逐行解析 nginx 反向代理配置介绍其余配置,在 nginx、Caddy 和 Traefik 之间进行选择介绍如何使用自动证书实现相同功能。
无论选择哪种代理,都应在防火墙中关闭 3080,使唯一的访问路径是经过身份验证的代理。ufw 防火墙基础介绍相关规则。通过 TLS 使用基本身份验证只是最低限度的保护,不是完整的安全模型:持有该密码的人就拥有服务器上的 shell。条件允许时,优先使用隧道。
如何更改 dsh Web 服务监听的端口?
--port 属于 Web 应用,而不是启动器。CLI 文档直接给出了示例:
dsh --profile web --port 8080dsh web 是 --profile web 的别名,因此 dsh web --port 8080 是相同的命令。启动器只解析自己的选项,并将其后的所有内容传递给已启动的配置文件。启动器选项必须放在前面;启动器无法识别的第一个标记会开始作为应用程序参数。将 --port 放在配置文件之后,不能放在配置文件之前。
请读取命令输出的 URL,不要自行假定端口,因为该行显示的是服务器实际绑定的地址。然后更新隧道配置的最后一个字段,使其匹配:
ssh -N -L 3080:127.0.0.1:8080 you@your-vps如需永久更改端口,应在配置文件中设置,而不是使用命令行选项。web 和 headless 配置文件首次使用时会根据随附模板自动初始化,文件位于 ~/.dsh 下。API 密钥和模型端点设置也位于同一目录中,因此,如果您已经要编辑这些文件,请一并阅读配置 dsh 密钥、模型和端点。要查看所有配置层合并后的实际生效配置:
dsh --dump-configWeb 服务器插件只提供两个键:host 和 port。将 port 设置为 0 会请求操作系统分配一个空闲端口,文档将其说明为“zero requests an OS-assigned port”。这样可以避免端口冲突,但不适合用于隧道,因为端口号会在每次重启时变化。
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介绍了无需推理过程的隧道配置。
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 是运行 shell 命令和编辑文件的代理控制界面,相关操作使用运行 dsh 的用户权限;而开发者预览版完全没有登录界面。将其绑定到所有网络接口后,任何能访问 3080 端口的人都可以在您的服务器上执行命令。保持绑定地址为 127.0.0.1,在防火墙中关闭 3080,并使用 SSH 隧道、私有覆盖网络,或要求密码验证的反向代理。
关闭 SSH 会话后,如何让 dsh Web UI 继续运行?
前台运行的 npx @deepseek-ai/dsh web 是登录 shell 的子进程,因此该 shell 退出时它也会被终止。在 tmux 会话中启动它,然后使用 Ctrl-b d 分离会话;或者将其作为启用 lingering 的 systemd 用户服务运行。隧道和 harness 相互独立:只要 harness 本身有一个生命周期长于登录会话的父进程,您可以随时断开并重新建立 SSH 隧道,而不影响正在运行的 harness。
通过 nginx 运行长时间的代理任务时,为什么 dsh Web UI 会在中途冻结?
因为 nginx 的默认 proxy_read_timeout 为 60 秒。连接连续一分钟没有数据时,nginx 会关闭该连接,而长时间运行的代理步骤很容易出现这种情况。在 location 块中设置 proxy_read_timeout 3600s;。添加 proxy_buffering off;,使输出在产生后立即流式传输到浏览器;同时通过 proxy_http_version 1.1; 传递 Upgrade 和 Connection 标头,以确保 WebSocket 握手成功。缺少这些标头时,页面可以加载,但始终不会收到更新。