适用场景:你有一台服务器,想把 DeepSeek Harness 的浏览器界面( Web GUI )跑在上面,让家里的电脑、公司的电脑、甚至手机浏览器都能登录使用,用 AI 帮你写代码、跑任务。本文基于 DSH
0.1.0-rc.6+ Ubuntu 22.04/24.04 + nginx + pm2 的实际部署经验整理。
为什么需要服务器 + 反代
DSH 的 Web 界面(dsh --profile web)默认只监听 127.0.0.1:3080,而且出于安全考虑**禁止 --host 0.0.0.0**( CLI 直接报错:防止把远程代码执行能力暴露到公网)。所以正确姿势是:
浏览器(手机/电脑)──► nginx ( 80/443 )──► dsh web ( 127.0.0.1:3080 )
nginx 做反向代理 + 登录保护 + 移动端适配,DSH 本身只在本机回环地址提供服务。
架构总览
- dsh web:实际服务,监听
127.0.0.1:3080 - dsh-webui-auth 插件: 登录认证(未认证的浏览器拿不到任何资源)
- pm2: 守护进程 + 开机自启
- nginx: 对外入口,反代 + 移动端缩放 + manifest 直出
第一步:安装 DSH
需要 Node.js ^22.19.0 || >=24.0.0:
npm install -g @deepseek-ai/dsh
dsh --version # 验证安装
第二步:初始化 web profile 并安装认证插件
dsh --profile web 首次运行时自动初始化 profile (目录在 ~/.dsh/profiles/web)。先装登录认证插件(需要 pnpm ,Node 自带 corepack ):
corepack enable pnpm
npx @deepseek-ai/dsh plugin --profile web add dsh-webui-auth
之后启动 web 服务,首次访问登录页时创建你的账号密码(存在 ~/.dsh/profiles/web/node_modules/dsh-webui-auth/dsh-webui-auth.json,密码是 scrypt 哈希):
dsh --profile web
补充:固定目录选择器为浏览器模式(服务器远程访问必做)
创建 ~/.dsh/patch.yml:
# 固定目录选择器为「浏览器浏览」模式。
# 默认的 directory-picker-auto 会根据服务器环境自动选择:
# - native:系统目录对话框,需要在服务器本机的显示器上操作;
# - browse:浏览器内的目录列表(可浏览/新建子目录)。
# 服务器通过 nginx 反代远程访问时,native 对话框会弹在服务器自己的屏幕上,
# 远程的手机/电脑根本看不到,必须固定为 browse 。
# 注意:patch 无法直接改已有行的插件 name ,所以先禁用 auto 行,
# 再插入 browse 的主机端 + 客户端插件对。
- id: directory-picker
disabled: true
- insert:
- id: directory-picker-browse
name: '@deepseek-ai/dsh-host-directory-picker-browse'
- id: directory-picker-browse-ui
name: '@deepseek-ai/dsh-client-ui-directory-picker-browse'
启动时用 --patch 挂上(见第四步的启动参数)。如果服务器上确实没有
DISPLAY/chooser 环境,auto 也会选 browse ,但显式固定可以保证任何环境下行为一致。
第三步:配置 API Key
在 ~/.dsh/.credentials.yaml 写入:
DEEPSEEK_API_KEY: sk-你的密钥
(或通过环境变量 DEEPSEEK_API_KEY 注入,二选一即可。)
第四步:pm2 守护 + 开机自启
写 pm2 配置 ~/.dsh/pm2.json:
{
"apps": [
{
"name": "dsh-web",
"script": "/usr/local/bin/dsh",
"args": "--profile web --patch /root/.dsh/patch.yml --trusted-host 你的域名",
"autorestart": true,
"restart_delay": 3000
}
]
}
--trusted-host 你的域名必须加:Web 界面调用/api时有浏览器信任围栏,通过域名访问时 Host 不再是127.0.0.1:3080,不加这个参数 API 会被拒。
启动并设置开机自启:
pm2 start ~/.dsh/pm2.json
pm2 save
pm2 startup
第五步:nginx 反向代理
新建 /etc/nginx/sites-available/dsh.apple.me(域名换成你的),软链到 sites-enabled 并 reload:
ln -s /etc/nginx/sites-available/dsh.apple.me /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
下面这份是完整最终配置,包含两个必踩的坑的解法(见注释),gzip/缓存为可选优化:
nginx 配置太长,点击这里访问我的博客,里面有 nginx 配置
坑 1:手机浏览器内容区域太小
现象:手机上打开 Web 界面, 工作区域宽度太窄,设置页面几乎没法用。
原因:页面 HTML 里的 viewport meta 是 initial-scale=1, 文字太大。
解法:在 nginx 上用 sub_filter 把响应里的 viewport 改成 0.7,让页面在手机屏上缩小显示。两个要点:
- 必须先
proxy_set_header Accept-Encoding "";把发给上游的压缩请求头置空,否则上游返回 gzip 压缩的 body ,sub_filter的字符串替换会失效; sub_filter默认只处理text/html,HTML 页面正好符合。
改完 nginx -t && systemctl reload nginx,手机硬刷新(清缓存)后就能看到效果。
坑 2:manifest.webmanifest 请求永远是 302 ,PWA 装不上
现象:浏览器加载 <link rel="manifest"> 时请求 /manifest.webmanifest 一直被重定向到登录页( 302 ),DevTools 里 manifest 解析失败,手机"添加到主屏幕"不可用,display 模式也控制不了。
原因:dsh-webui-auth 插件在 HTTP 层拦截了所有未认证请求(包括静态资源)并 302 到登录页。而浏览器拉取 manifest 的请求不会携带登录 Cookie ,于是永远 302 ,拿不到 manifest 内容。
解法:让 nginx 绕过认证,直接输出这份 manifest 文件——内容照抄 DSH dist 里的 manifest.webmanifest,并把 "display": "fullscreen" 改成 "standalone"( standalone 是"普通应用窗口",手机 PWA 更合适; fullscreen 会全屏隐藏浏览器 UI )。
要点:
- 用
location =精确匹配,只接管这一个路径,不影响其他资源; - 内容硬编码在 nginx 里,以后 DSH 升级如果改了 manifest 结构需要手动同步;
- 不用改 DSH 源码,也不用给认证插件开白名单,改动全部在 nginx 层,升级 DSH 不会丢。
访问与验证
- 电脑:浏览器打开
http://你的域名,登录后正常使用; - 手机:同样地址登录。验证 PWA:Chrome 菜单 →「添加到主屏幕」; DevTools → Application → Manifest ,
display应为standalone; - 如果手机或电脑上看到的是旧页面,记得硬刷新清缓存。
常见问题
- 想用 HTTPS?
用 certbot 申请证书,
listen 443 ssl,并把 80 重定向到 443 (注意 443 的 location 也要带上同样的 sub_filter 和 manifest 直出配置)
- 改完 nginx 不生效?
nginx -t先查语法,再systemctl reload nginx,浏览器硬刷新
- 远程访问时"选择目录"没反应,或对话框弹在服务器本机屏幕上?
检查启动参数里是否带了
--patch /root/.dsh/patch.yml固定 browse 目录选择器;不固定时 auto 可能选中 native (系统对话框弹在服务器屏幕上,远程用户看不到也点不了)