← All Guides

Guide

DeepSeek Harness VPS Hosting: Run dsh web Remotely

DeepSeek Harness binds its Web UI to loopback on purpose, prints a one-time token in the URL, and refuses to listen on every interface. That shapes the whole server setup: you run it on the VPS and tunnel to it. This guide covers the documented way to do that, from the official repository.

Why run DeepSeek Harness on a server

dsh works on whatever directory you launch it from, and its sessions are persisted. Put it on a VPS and the sessions, the repository checkout and any long job live on an always-on machine instead of your laptop. You can start a refactor at your desk, close the lid, and read the result from a phone. The model still runs at the provider, so the server is a thin client with a shell attached.

If you have not installed it before, start with the DeepSeek Harness install guide for the first-run steps (API key, choosing a workspace). If you landed here searching for a desktop app, read the desktop guide first. This page is only about running it remotely.

Developer preview: the README says to expect compatibility-breaking changes, and the project's SAFETY.md says it has not been through a security audit. Both are reasons to put it on a dedicated VPS rather than a machine that holds anything you cannot lose.

What the server needs

The docs publish no RAM or CPU minimum. The runtime requirement that is documented is Node.js: the repository's engines field is ^22.19.0 || >=24.0.0. The latest npm tag was 0.2.0-rc.2 when we checked on 7 October 2026, so you are running a release candidate.

Size the box for what the agent will run, not for dsh. If it only edits files and calls an API, a small plan is enough. If it will build, test or start your own services, give it 2 vCPU and 4 GB of RAM. Linux also gets you a real sandbox backend: the reference describes bwrap, Landlock and Seatbelt as the platform backends, with bwrap running commands in a private PID namespace.

Set it up, step by step

1. Create a non-root user and do everything below as that user. The agent runs shell commands with that account's permissions.

adduser --disabled-password dsh
mkdir -p /home/dsh/.ssh && cp ~/.ssh/authorized_keys /home/dsh/.ssh/
chown -R dsh:dsh /home/dsh/.ssh

2. Install Node 22.19+ or 24 with nvm, fnm or your distribution's NodeSource packages, then check node --version.

3. Put the API key where the docs say. dsh reads DEEPSEEK_API_KEY, and Web search uses it too. Use the home-level dotenv so the key never lands in a repository:

mkdir -p ~/.dsh
read -rs -p 'DeepSeek API key: ' KEY && echo
umask 077 && printf 'DEEPSEEK_API_KEY=%s
' "$KEY" > ~/.dsh/.env
unset KEY

The alternative is to enter it in Settings → Models once the UI is up; it is then stored in $DSH_HOME/.credentials.yaml. See our DeepSeek API key guide if you do not have one.

4. Start it in the project directory. The invoking directory is the default workspace root, so cd into the repository first.

cd ~/projects/my-repo
npx @deepseek-ai/dsh web --no-open

Over SSH the browser handoff is skipped anyway, and --no-open makes that explicit. The command prints a dsh web: line with the URL and its token. Keep that line; you need it in the next section. Port 3080 is the default and --port changes it.

5. Pin a version for repeatable servers. Run npx @deepseek-ai/[email protected] web, or install it with npm install -g @deepseek-ai/dsh (the package exposes a dsh bin) and upgrade by installing a newer tag. Because the project warns about breaking changes, upgrade deliberately and look at the output of dsh --version afterwards.

Reach dsh web without exposing it

By default the GUI listens on loopback and accepts connections from that machine only. The web app README states plainly that binding all interfaces is unsupported: --host 0.0.0.0 is rejected at startup. That is the property you want, so forward a port instead.

# on your laptop
ssh -N -L 3080:127.0.0.1:3080 dsh@your-vps

Now open the URL from the server's dsh web: line in your laptop browser. It points at 127.0.0.1:3080, which the tunnel carries to the VPS. This is why the printed URL still works: authentication is the token in that URL, which the browser exchanges for a signed session cookie and is then redirected to the same address without the token. The Host fence admits loopback and the tunnel does not change the token.

What you seeWhat it means
Page rejects you or asks to reopen the printed URLYou opened the address without the token. Use the full dsh web: line from this run; the token is fresh for each process.
403 on every API callThe browser reached dsh under an authority the fence does not accept. Loopback is accepted by default; any other host name must be listed with --trusted-host.
Page loads, composer disabledNo workspace is selected yet. Click Choose workspace.
Connection refused on your laptopThe tunnel is not up, or dsh is not running on the server.

From a phone. Use an SSH app that supports local port forwarding and forward 3080 to 127.0.0.1:3080, then open the token URL in the phone's browser. If the VPS is on a Tailscale network, make the SSH hop over the tailnet name (ssh -N -L 3080:127.0.0.1:3080 dsh@your-vps-tailnet-name), so you can close the public SSH port as well. The dsh port itself stays on loopback.

Public URL behind a proxy. The project does document a reverse-proxy route: --public-url advertises the address, --trusted-host names the authority browsers use, and the proxy must preserve Host, strip the mount prefix, forward WebSocket upgrades, rewrite cookie scope and terminate TLS. Its own docs add that neither flag protects the port, and that the printed URL carries a process credential. That is a team deployment with a proxy you maintain, not something to set up to avoid typing an ssh command.

Keep it running

tmux is enough for a personal box:

tmux new -s dsh
cd ~/projects/my-repo && npx @deepseek-ai/dsh web --no-open
# detach with Ctrl-b d; tmux attach -t dsh to read the URL line again

systemd restarts it after a reboot. Use the absolute path that command -v dsh prints for the user, and the repository as the working directory, since that is the workspace root. dsh starts through #!/usr/bin/env node and systemd does not read your shell profile, so the unit's PATH must contain the directory of command -v node (with nvm or fnm that is under your home directory, not /usr/bin):

[Unit]
Description=DeepSeek Harness web UI
After=network-online.target

[Service]
User=dsh
WorkingDirectory=/home/dsh/projects/my-repo
Environment=PATH=/usr/local/bin:/usr/bin:/bin
ExecStart=/usr/local/bin/dsh web --no-open
Restart=on-failure

[Install]
WantedBy=multi-user.target

Read the token URL with journalctl -u dsh; it is new after every restart. For scheduled or CI-style work, skip the web server altogether: dsh --profile headless "run the tests" persists one session, prints the final message and exits 0 or 1, and mounts no HTTP server or port. Since 0.1.7 it also takes --json and --session-id.

Sandbox, approvals and what leaves the box

New sessions default to the workspace-write permission preset with the ask approval policy: writes are limited to the workspace and temp roots, and the Web UI asks before operations that need approval. read-only denies writes. danger-full-access bypasses confinement and pairs with the never approval policy, so do not pick it on a server that holds anything else. DSH_PERMISSION_MODE sets the process fallback.

Two things matter more on a server than on a laptop. Reads and network access are not confined even in workspace-write, so the agent can read any file that user can read. And per the CLI reference, the base bundle ships with a default-on DeepSeek session-log upload and a feedback-gated OpenTelemetry upload, so check the Settings pages if you work on private code. A HTTPS_PROXY variable is honored when your server needs an outbound proxy.

VPS versus managed: what it costs

Prices checked October 2026. Model usage is billed by DeepSeek on top of every row.

OptionSpecPrice
OVHcloud VPS-12 vCores, 4 GB, 40 GB$4.54/mo billed annually
Hostinger KVM 22 vCPU, 8 GB, 100 GB$8.99/mo on a 2-year term (renews $14.99)
Hetzner CPX222 vCPU, 4 GB, 80 GBEUR 19.49/mo before VAT
Vultr2 vCPU, 4 GB, 80 GB$20/mo
DigitalOcean Basic2 vCPU, 4 GiB, 80 GiB$24/mo
OpenClaw Launch Lite1 vCPU, 2 GB, 10 GB, 1 instance$3 first month, then $6/mo ($60/yr)
OpenClaw Launch Pro2 vCPU, 4 GB, 40 GB, up to 3 instances$20/mo ($200/yr)

The VPS gives you root and the cheapest long-term price. In exchange you own the user setup, the tunnel, the restart policy, node upgrades, and every rc-to-rc breaking change.

The managed option: Coding Harness

OpenClaw Launch's Coding Harness is a hosted Linux box opened in your browser, with eight coding CLIs pre-installed: Claude Code, Codex, OpenCode, Aider, OpenClaude, Zero, Pi and DeepSeek Harness (dsh). Pick one on the workspace card; they all share the same persisted /workspace directory, there is a real browser terminal, and Reset and Wipe are available. dsh signs in the way it does anywhere else, with DEEPSEEK_API_KEY or its own settings, and your DeepSeek account is billed for the model. A workspace uses one of your plan's instance slots at normal plan pricing, and the free trial is 30 minutes with no credit card. Details are on the coding workspace page.

OpenClaw Launch also hosts chat agents (OpenClaw and Hermes Agent) on Telegram, Discord and WhatsApp; see Hermes hosting and the hosting overview. A coding workspace is a developer environment, not a chatbot. If you are weighing dsh against a hosted personal agent, the OpenClaw vs DeepSeek Harness comparison lays out the difference.

Frequently Asked Questions

Can I run DeepSeek Harness on a VPS?

Yes. dsh is a Node.js command, and the repository's engines field asks for Node 22.19 or newer, or 24 and up. It needs no GPU because the model runs at DeepSeek's API (or whichever provider you configure). The official README even covers the case: when you launch the Web UI over SSH it prints the host URL instead of opening a browser, because your SSH client owns the local forwarded address.

How do I reach dsh web from my laptop?

Keep dsh bound to its default loopback address and forward the port: ssh -N -L 3080:127.0.0.1:3080 user@your-vps, then open the full URL that dsh printed on the server, token included, in your laptop browser. The listener accepts loopback by default, so the tunnel needs no extra flags. If you open the bare address without the token, you will not be signed in.

Can I bind dsh web to 0.0.0.0 so I can use the server IP?

No, and you cannot try: the web app documents that --host 0.0.0.0 is rejected at startup for safety. The listener also has no TLS. The documented remote route is a TLS-terminating reverse proxy with --public-url and --trusted-host, and even then the project says to restrict the listening port to the trusted proxy or network. For a single developer, an SSH tunnel is simpler and keeps the port closed.

Why does the URL change every time I restart dsh web?

The startup line carries a fresh process token, and the browser trades that token for a signed cookie on first load. After a restart, read the new dsh web: line from your tmux pane or from journalctl and open that one. Setting printUrl to false suppresses the line entirely, so leave it on for a server you reach remotely.

How do I keep dsh running after I close SSH?

Run it in tmux, or write a small systemd service that runs as your non-root user with the repository as its working directory. dsh web is a foreground process, and SIGINT or SIGTERM dispose it cleanly. For one-shot jobs, dsh --profile headless runs a task, prints the final answer and exits 0 on completion or 1 otherwise, and it opens no listening port.

Is it safe to let the agent run on my server?

Treat it as a user who can run shell commands. The default permission preset is workspace-write with the ask approval policy, which confines writes to the workspace and temp directories, but reads and network access are not confined. The project's own SAFETY.md says it has had no security audit, that sandboxing does not guarantee isolation, and recommends a disposable VM or container. Use a dedicated box with no other secrets on it.

Where does dsh store my DeepSeek API key on a server?

Provider credentials resolve in this order: the inherited environment, $DSH_HOME/.credentials.yaml (a key saved in Settings > Models), the invoking directory's .env, then $DSH_HOME/.env. DSH_HOME defaults to ~/.dsh. On a server, putting DEEPSEEK_API_KEY in ~/.dsh/.env (mode 600, typed at a hidden prompt rather than on the command line) keeps it out of your repository and your shell history.

Can I get dsh without running a server myself?

Yes. The Coding Harness on OpenClaw Launch is a hosted Linux workspace with dsh pre-installed next to seven other coding CLIs, opened in a browser terminal. You sign in to DeepSeek with DEEPSEEK_API_KEY or dsh's own settings, and model usage is billed by your DeepSeek account. See Coding workspace hosting.

Checked against the official repository (README, SAFETY.md, the web app and CLI references, and the reverse-proxy guide) and the npm package metadata on 7 October 2026.

Related Guides

Skip the server

A hosted Coding Harness with dsh pre-installed. Free 30-minute trial, no credit card. Lite is $3 for the first month, then $6/month.

See the Coding Harness