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 正在监听的位置,以及允许访问它的对象。只有运行该 Harness 的计算机 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 的用户身份执行这些操作。
因此,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 仍会成功连接。这样会得到一个正常的会话,但隧道已经失效,且不会收到警告。ServerAliveInterval=30 每 30 秒发送一次 keepalive,使空闲隧道能够在咖啡馆和酒店路由器的 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 下自动初始化。要查看所有配置层合并后的实际生效内容:
dsh --dump-configWebserver 插件只提供两个键: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 处于开发者预览阶段,迭代速度很快,后续会有不兼容的变更。
npx @deepseek-ai/dsh web 每次运行时都会解析为最新发布版本。即使服务器一周未改动,下次启动时也可能运行带有不同标志的 CLI。请固定版本,避免重启变成升级:
npx @deepseek-ai/dsh@0.1.0-rc.7 web截至 2026 年 8 月,已发布的软件包版本为 0.1.0-rc.7。接受默认版本前,请先检查不带参数的 npx 会拉取哪个版本:
npm view @deepseek-ai/dsh version在预览版之间,标志可能会在启动器和 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 是代理的控制界面。该代理会以运行 dsh 的用户身份执行 shell 命令和编辑文件,而且开发者预览版完全没有登录界面。将其绑定到所有接口后,任何能访问 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 秒。连接连续 1 分钟没有数据时,nginx 会关闭该连接,而长时间运行的代理步骤很容易出现这种情况。在 location 块中设置 proxy_read_timeout 3600s;。添加 proxy_buffering off;,让输出到达后立即流式传输到浏览器;同时通过 proxy_http_version 1.1; 传递 Upgrade 和 Connection header,使 WebSocket 握手成功。缺少这些 header 时,页面可以加载,但始终收不到更新。