DeepSeek Harness (DSH) Docker 部署指南
概述
本仓库是 DeepSeek Harness 源码 + Docker 构建配置,用于将 dsh 打包为镜像并以 Web 模式(浏览器 UI)运行,默认端口 3080。
构建流程(对应原始命令 git clone → pnpm install → pnpm run build → pnpm dsh web):
┌──────────┐ http://localhost:3080 ┌──────────────────────────┐
│ 浏览器 │ ────────────────────────▶ │ dsh web (Node 24) │
└──────────┘ │ 监听容器内 0.0.0.0:3080 │
└────────────┬─────────────┘
┌───────────────────┼───────────────────┐
│ dsh-data 卷 │ ./workspace 挂载 │
│ /home/dsh/.dsh │ /workspace │
│ (会话/设置/配置) │ (agent 工作目录) │
└───────────────────┴───────────────────┘
镜像信息
已构建并推送的多架构镜像 registry.cn-hangzhou.aliyuncs.com/skymirror/dsh:latest(npm 安装路径镜像):
| 项目 | 值 |
|---|---|
| DSH 版本 | 0.1.2-alpha.2-skymirror.1(源码 commit 4d35ae4dee,同步自上游 dsh-v0.1.2-alpha.2) |
| 构建时间 | 2026-09-01 03:22 UTC(北京时间 2026-09-01 11:22) |
| 支持架构 | linux/amd64、linux/arm64 |
| manifest list | sha256:4c8636d1f20804a39757829de5adb27dccf6435b99610e2a70e74fe8cc95b5bd |
| amd64 digest | sha256:c7e0942b8dc38aa8787def4ba634ac967d767605d678ce010712df74e7195a2d |
| arm64 digest | sha256:ea1226c71ddeb1675ed9355524dbe6ec5bd0f0431ce0a36bff9de63a7730779f |
| 镜像大小 | 单架构解压 ~0.91GiB(amd64 977,603,541 / arm64 977,698,548 字节实测;上一版全量镜像 2.7GB,缩减 64%),注册表压缩 amd64 302MiB / arm64 297MiB(上一版 ~0.9GB) |
| 镜像来源 | dist/ npm pack 产物直接安装:生成器计算 CLI 依赖闭包(224 个 fork 包)以 file: 依赖离线安装,第三方依赖走 npmmirror;镜像内无 monorepo 源码(无 packages/ 工作区、无 pnpm-lock.yaml) |
| 内置环境 | Node.js 24.19 / Python 3.11 / pnpm 11.7.0 / Rust 按需安装 / gcc·g++ 12,详见下文「镜像内置语言运行环境」;JDK 与 Go 不再内置 |
历史构建存档(已被上表取代,哈希保留备查):
| 项目 | 值 |
|---|---|
| 历史 manifest list | sha256:8a5d6406abd56f5cc3f175aa6b751e226bc91430dfb896add1fcf9a3f970f39e(2026-09-01 00:15 UTC,monorepo 源码编译镜像,单架构解压 ~2.7GB) |
| 历史 amd64 digest | sha256:c09248d31d641967fec21d78529ab41ee596c402545e0fd4cbeead17b63d41b5 |
| 历史 arm64 digest | sha256:7295a2e5fcafa933c497b3eaa9fae279aeb85e0b6f44793602fd09f3046d0247 |
快速开始(使用已构建镜像)
docker pull registry.cn-hangzhou.aliyuncs.com/skymirror/dsh:latest
docker run -d --name dsh \
-p 0.0.0.0:3080:3080 \
-v dsh-data:/home/dsh/.dsh \
-v "$PWD":/workspace \
-e DSH_TRUSTED_HOSTS=your-server-ip \
registry.cn-hangzhou.aliyuncs.com/skymirror/dsh:latest
# 浏览器认证:启动日志会打印 dsh web: http://<host>:3080/?token=<一次性令牌>。
# 执行 docker logs dsh 找到该行,把主机名换成 your-server-ip 后在浏览器打开;
# token 换取 30 天签名 cookie,之后在 Web UI 中配置模型 API Key 即可使用。
本次镜像包含的修复与变更
- npm 安装路径:镜像不再编译 monorepo——构建时把
dist/的 npm pack 产物复制进镜像,docker/install-dsh-packages.mjs从 tarball 元数据计算 CLI 依赖闭包(224 个 fork 包,testkit/examples/experimental 及其依赖的 ~616MB AI SDK 不进镜像),以file:依赖离线安装,registry 只服务第三方依赖;镜像内无packages/工作区与pnpm-lock.yaml。 - 体积与构建时间:镜像从全量编译的 ~2.7GB(解压)降到 ~0.91GiB(-64%),镜像构建只剩 apt 层 + npm install,显著快于 pnpm install + tsc/tsdown + vite 全量编译。
- 双架构构建:buildx 一次产出
linux/amd64与linux/arm64两个平台的镜像。 - 容器外访问:
dsh web默认绑定0.0.0.0:3080(上游默认只监听127.0.0.1),并保留DSH_TRUSTED_HOSTS(显式信任,设置后替代上游自动信任局域网地址的行为)与DSH_TRUST_ALL(请求级逃生口:同时跳过 Host/Origin 围栏与浏览器认证)两道开关。 - 浏览器认证:随上游 0.1.2 采纳 BrowserAuth——启动 URL 携带一次性 token,仅在
GET /有效,换取 30 天签名 cookie;此后每个/api方法与 WebSocket stream 都要求该 cookie。 - 远程配置设置:
settings/credentials配置面对已认证的远程浏览器开放(浏览器认证统一覆盖所有方法,特权集机制上游已移除);客户端设置持久化固定host,远程浏览器偏好持久化。访问控制由浏览器认证 + 上层 NGINX 承担;原生能力(目录选择器、打开设置文件、agent preset 创作、llm.discoverModels)不再有方法级回环钉死,但 UI 中的「打开设置文件」动作仍仅在本机浏览器展示。 - 插件原生构建:内置
allowBuilds预置,修复 pnpm 默认拦截原生依赖构建导致带 node-gyp 编译的插件(如 node-pty)安装失败的问题。 - 构建稳健性:构建命令保留
DSH_CLIENT_COMMIT_HASHbuild-arg 与--provenance=false --sbom=false(后者修复阿里云 ACR 拒绝 OCI attestation 清单导致推送失败的问题)。 - 工具链调整:JDK/Go 不再内置(npm 路径镜像以精简为先),Python 3.11(apt 预编译)与 Rust 按需安装保留。
发布到 registry 后的重建路径(一行):fork 版本真实 publish 后,把 Dockerfile 中 COPY dist/ + 生成器安装换成 npm install -g @deepseek-ai/dsh@<dist-tag> --registry <私有 registry>(安装器语义不变),镜像即从 registry 拉取 fork 包,不再依赖本地 pack 产物。
前置条件
- Docker Engine 20.10+(支持 BuildKit 多阶段构建)
- Docker Compose v2(
docker compose命令) - 内存建议 ≥ 4GB(
pnpm build编译 Vite 前端较吃内存)
一、npm 安装(推荐,无需 Docker)
fork 版本已发布为 npm 包 @deepseek-ai/dsh(版本线 0.1.2-alpha.2-skymirror.*,发布在私有 registry),安装即用:前端产物随 @deepseek-ai/dsh-web-frontend 依赖分发,无任何构建步骤。
用户安装与启动
# 一次性配置私有 registry(放在用户级 ~/.npmrc 或项目 .npmrc 均可):
# registry=<私有 registry 地址> ← 由用户提供,如云效制品库 npm 仓库或 ACR npm 仓库
npx @deepseek-ai/dsh@alpha web # 直接启动 Web UI(默认 0.0.0.0:3080,打印带 token 的访问 URL)
# 或全局安装后常驻使用:
npm install -g @deepseek-ai/dsh@alpha
dsh web --port 8080
浏览器打开启动日志打印的 dsh web: http://<host>:<port>/?token=<一次性令牌>(token 换取 30 天签名 cookie)。远程访问需要通过 /api 信任围栏,环境变量用法与 Docker 部署一致:DSH_TRUSTED_HOSTS=your-server-ip(显式信任,设置后替代自动派生局域网地址)或 DSH_TRUST_ALL=1(请求级逃生口:同时跳过围栏与浏览器认证,仅限可信网络)。需要 dsh plugin 管理插件时先 corepack enable pnpm(web 启动本身不需要 pnpm)。
要求 Node.js ^22.19 || >=24(CLI 包已声明 engines,旧版本 npm 会直接拒绝安装)。
版本与 dist-tag
fork 预发布版本(-skymirror.N 后缀)发布时自动落在 alpha dist-tag 上(上游发布序列规则:prerelease 首段为 alpha 的版本映射 alpha tag,不占用 latest)。因此:
npm view @deepseek-ai/dsh dist-tags --registry <私有 registry 地址> # 查看 alpha/latest 指向
npx @deepseek-ai/dsh@alpha web # 显式按 alpha tag 启动
# 若希望 @latest 命中 fork 版本:npm dist-tag add @deepseek-ai/dsh latest <版本> --registry <地址>
发布 fork 包(维护者)
发布完全复用上游发布序列(打包与发布分离、幂等、同完整性跳过),registry 由环境注入(NPM_CONFIG_REGISTRY 环境变量或 .npmrc),不硬编码任何 registry 地址:
pnpm run release:dsh 0.1.2-alpha.2-skymirror.1 # 1. 版本落盘:写全部 manifest + lockfile 并提交(本地执行)
pnpm run build:official # 2. 官方构建(pack 校验构建记录)
pnpm run release:pack --family dsh --out dist/npm # 3. 打包 245 个 tarball 到 dist/npm
pnpm run release:pack --family vendor --out dist/npm-vendor # 4. vendor 家族(verify 需要)
pnpm --dir native/landlock-run run build:ts && \
pnpm --dir native/landlock-run/packages/entry pack \
--pack-destination "$PWD/dist/npm-landlock" # 5. Landlock entry(verify 需要)
pnpm run release:verify-packed-install --family dsh \
--from dist/npm --from dist/npm-vendor --from dist/npm-landlock # 6. 安装冒烟:--version + dsh web HTTP 200
NPM_CONFIG_REGISTRY=<私有 registry 地址> \
pnpm run release:publish --family dsh --from dist/npm # 7. 发布(幂等;真实发布需要云效制品库 NPM_TOKEN 或 ACR npm 仓库凭据,由用户提供)
上游 release:pack --family dsh 打包 245 个包共约 8MB(CLI 包 15KB,前端 dist 随 @deepseek-ai/dsh-web-frontend 1.4MB)。
换 scope 发 npmjs(可选项,暂不采用):fork 也可以全树改名后公开发布到 npmjs(如 @skymirror/dsh),无需私有 registry。不采用的代价:236+ 个 manifest 与 import 位改名,且每次上游同步都要重做;@deepseek-ai scope 的发布权在上游维护者,fork 包无法以原名发 npmjs。
npm 路径 vs Docker 方案
| npm 路径(npx) | Docker 镜像(npm 安装路径) | |
|---|---|---|
| 启动耗时 | npx 首次拉取约 15–20s(闭包 224 包,~290MB 安装体积),之后秒级 | 镜像拉取(压缩,较 npm 路径多一层基础系统),解压 ~0.91GiB |
| 构建耗时 | 无构建(前端 dist 随包分发) | 镜像构建 = apt 层 + npm install(不再全量编译 monorepo) |
| 内置工具链 | 仅 Node.js(需要 agent 编译环境时用 Docker) | Python/gcc·g++/pnpm 内置,Rust 按需(JDK/Go 不内置) |
| 升级 | npx @deepseek-ai/dsh@alpha web 每次自动拉新 | 重新 build/push/pull 镜像 |
| 数据目录 | $DSH_HOME(默认 ~/.dsh) | dsh-data 卷挂载 /home/dsh/.dsh |
| 适用 | 个人工作站/服务器快速上手、CI | 服务器常驻部署、离线环境、含原生编译链的 agent 场景 |
两者共享同一套运行时行为:0.0.0.0 默认绑定、DSH_TRUSTED_HOSTS/DSH_TRUST_ALL、浏览器 token 认证、$DSH_HOME 数据布局。
二、本地构建并运行(Docker)
git clone <仓库地址>
cd DSH
pnpm run build:official # 1. 官方构建(镜像安装的 pack 产物来自这一步)
pnpm run release:pack --family dsh --out dist/npm # 2. 产出 dist/ pack 产物(vendor/landlock 同理,见上文发布序列)
docker compose build # 3. 构建镜像:复制 dist/ tarball 进镜像并 npm install(无 monorepo 编译)
docker compose up -d # 4. 启动
# 从 docker logs dsh 复制带 ?token= 的 dsh web: URL,在浏览器打开(token 换取 30 天签名 cookie)
首次使用时在 Web UI 中配置模型 API Key(DeepSeek 或任意 OpenAI 兼容端点)。
清理 buildx 构建缓存
多架构构建(amd64 + arm64)会把每个大层各存两份,反复改 Dockerfile 后 buildx 不会自动回收被替换的旧层,缓存体积会持续膨胀(可达数十 GB)。构建/推送后定期清理即可回收,不影响已推送的镜像:
docker buildx du --builder multiarch # 查看缓存占用
docker buildx prune --builder multiarch --filter until=24h # 只清 24h 前的旧层(推荐)
# 或彻底清空:
# docker buildx prune --builder multiarch --all
三、用云效构建并推送镜像
在云效(Codeup)流水线里配置「镜像构建」步骤(Dockerfile 位于仓库根目录),构建完成后镜像会推送到 ACR(阿里云容器镜像服务)。
随后在目标机器上拉取并运行:
docker pull registry.cn-hangzhou.aliyuncs.com/skymirror/dsh:latest
docker run -d --name dsh \
-p 0.0.0.0:3080:3080 \
-v dsh-data:/home/dsh/.dsh \
-v /path/to/workspace:/workspace \
-e DSH_HOME=/home/dsh/.dsh \
registry.cn-hangzhou.aliyuncs.com/skymirror/dsh:latest
四、目录 / 端口映射说明
| 项目 | 说明 |
|---|---|
端口 0.0.0.0:3080 | 容器内 dsh web 监听 0.0.0.0:3080 |
数据卷 dsh-data → /home/dsh/.dsh | 会话记录、settings、storages、agent presets、profiles(插件)、attachments 持久化 |
./workspace → /workspace | agent 的工作目录,编辑宿主机文件实时生效 |
插件目录 $DSH_HOME/profiles/<name> | profile / bundle / cordis.patch.yml 插件的安装位置,位于 dsh-data 卷内持久化 |
dsh 命令 | 已软链到 /usr/local/bin/dsh,供 docker exec 安装与管理插件 |
$PNPM_HOME | /home/dsh/.dsh/pnpm,pnpm 全局目录落在 dsh-data 卷内,不依赖 /workspace 是否挂载 |
$DSH_HOME | /home/dsh/.dsh,DSH 所有用户数据的根目录 |
$HOME | /workspace,让 Web UI 目录选择器默认从 /workspace 开始浏览 |
四·五、镜像内置语言运行环境
镜像内已预装以下环境,agent 可直接编写并运行对应代码:
| 语言/工具 | 版本 | 来源 | 说明 |
|---|---|---|---|
| Node.js | 24.19.0 | 基础镜像 | 含 npm |
| npm | 11.17.0 | npm 全局安装 | |
| pnpm | 11.7.0 | corepack | dsh plugin add 内部依赖,allowBuilds 预置随镜像提供 |
| Python | 3.11 | apt(Debian bookworm) | 预编译、glibc 一致,含 pip/venv/Python.h |
| Rust | stable(按需安装) | rustup-init 引导程序 | 首次运行 cargo/rustc/rustup 时自动装进 dsh-data 卷,见下文 |
| gcc / g++ | 12(bookworm 最新) | apt | 编译 C/C++ 及带 C 扩展的 Python 包 |
| git / curl | apt | 供 agent 拉依赖、编译 |
npm 安装路径镜像不再内置 JDK 17 与 Go 1.26.6(体积优先的取舍,两个工具链合计约 600MB)。需要 Java/Go 的 agent 任务可进容器后自行安装(apt 或官方 tarball),或在此 Dockerfile 基础上按需加回。
云效构建机在香港,网络可直连国际源(npm、PyPI、crates.io、GitHub、Docker Hub 均走官方源,无需镜像)。本地/国内构建仍默认走国内镜像(npmmirror + USTC apt/Rust/pip)。rustup 镜像源已从清华切换到 USTC(清华 rustup 镜像路径已下线)。
构建注意:Python 用 apt 的
python3(3.11,与容器 glibc 一致)而非源码编译;npm install 的 cache 定向/tmp并在层内清除,不进镜像层。python/pip已软链到/usr/local/bin;cargo/rustc/rustup是首次调用自动安装的包装器;dsh由镜像构建末尾软链到/usr/local/bin。
Rust 按需安装
镜像不内置完整 Rust 工具链(省 ~1GB),只内置 rustup-init 引导程序和 /usr/local/bin/ensure-rust 包装器。首次调用 cargo、rustc 或 rustup 时,包装器会自动把最小 stable toolchain 装进 $RUSTUP_HOME(/home/dsh/.dsh/rustup)和 $CARGO_HOME(/home/dsh/.dsh/cargo),两者都在 dsh-data 卷内,重启后保留。toolchain 与 crates 依赖默认走清华镜像(RUSTUP_DIST_SERVER + cargo config.toml 稀疏索引)。首次安装需要容器能访问网络;也可提前手动执行 docker exec dsh cargo --version 触发安装。
五、自定义配置
修改端口
编辑 docker-compose.yml,把 --port 传给容器:
command: ["--port", "8080"]
或直接改端口映射两侧的值。
从其它机器访问
docker-compose.yml 已把端口映射为 "3080:3080"(宿主机所有接口),容器内 dsh web 默认监听 0.0.0.0:3080。非本机浏览器需要过两道门:
/api浏览器信任围栏(可达性):DSH_TRUSTED_HOSTS只信任指定地址(多个用逗号分隔;设置后不再自动信任局域网地址),DSH_TRUST_ALL=1跳过 Host 校验(仅限可信网络)。- 浏览器认证(身份):从启动日志
docker logs dsh找到dsh web: http://…/?token=…行,把主机名换成服务器地址后在浏览器打开;token 只在根路径有效,成功后写入 30 天签名 cookie,之后正常使用。例外:DSH_TRUST_ALL=1时第 2 步整个跳过——该变量是请求级逃生口,同时跳过围栏与浏览器认证,仅供反向代理已完成全部调用方认证的可信网络使用。
environment:
# 方式一(推荐):只信任指定地址,多个用逗号分隔;浏览器仍需 token 引导
- DSH_TRUSTED_HOSTS=your-server-ip-or-domain
# 方式二(免配置,跳过围栏+浏览器认证):仅限 NGINX 等已统一认证的可信网络
# - DSH_TRUST_ALL=1
⚠️ 警告:DSH 具备执行代码能力。
DSH_TRUST_ALL=1会同时关闭防 DNS-rebinding / 跨站请求的 Host 校验与浏览器认证,只能用于上游带认证反向代理的可信内网,切勿裸暴露公网。
ℹ️ 注意:设置页(模型 / API Key 配置)向已认证的远程浏览器开放。浏览器认证统一覆盖所有
/api方法,远程浏览器完成 token 交换后即可读写模型与 API Key 配置,其设置偏好持久化保存。传输安全与入口认证请在 DSH 前用带认证 / TLS 的反向代理(如 NGINX basic-auth)挡一层,不要把裸端口直接暴露公网。
挂载自己的 .dsh 目录
volumes:
- /data/dsh:/home/dsh/.dsh # 替换命名卷为宿主机目录
- ./workspace:/workspace
安装与加载外部插件
插件(cordis.yml 插件、bundle、profile)安装在 $DSH_HOME/profiles/<name> 下,位于 dsh-data 卷内,重启后保留。容器内 dsh 已软链到 PATH,可直接执行:
docker exec -it dsh dsh plugin --profile web add github:user/plugin
docker exec -it dsh dsh --profile web
带原生依赖(node-gyp 编译)的插件在安装时编译:镜像运行时已内置 gcc/g++/make/python3/npm/pnpm/git。
第三方插件市场(如 dsh-market)自行维护自己的 pnpm 安装目录,不读 profile 的 allowBuilds 预置,因此带原生依赖的插件(node-pty)会触发 pnpm 的构建脚本拦截——按其提示执行一次 approve-builds(或等效命令)放行 node-pty 即可。它的 catalog/registry 抓取走它自己的默认地址,需要容器能对该地址出网;国内网络无法直连时,要么配置代理,要么让该工具改走可达的镜像源。
六、常见问题
| 现象 | 处理 |
|---|---|
| 构建报内存不足 | 增大 Docker 内存配额 |
| 端口被占用 | 修改 docker-compose.yml 端口映射 |
| 页面打不开 | docker logs dsh 看是否打印 dsh web: http://…/?token=… 行 |
/api 请求 401(要求认证) | 浏览器地址未带 token:从 docker logs dsh 复制带 ?token= 的 URL 打开(主机名换成服务器地址) |
/api 请求 403 | 非本机访问需设置 DSH_TRUSTED_HOSTS 为你的访问地址(或可信内网设 DSH_TRUST_ALL=1) |
crypto.randomUUID is not a function | 老镜像,重新构建(已在上游补丁修复) |
评论