DeepSeek Harness: Self-Hosted Agent Runtime, Measured

I installed DeepSeek's agent runtime on my own server, put it behind nginx and paid for a real headless run. These are the numbers, the traps and who should run it.

DeepSeek Harness: Self-Hosted Agent Runtime, Measured

It took twelve seconds and 12,158 input tokens to count two shell scripts in a folder. That was the first real task I gave DeepSeek's new agent runtime on my server. The answer was right. The token count was not.

The project has 263,933 GitHub stars eleven weeks after it was created, from the lab that also ships the model. I've done 30+ Nextcloud, Jitsi and Matrix deployments for clients, and they don't ask me whether a tool is popular. They ask whether it's safe to run on a box they own. So I installed it on a clean server, put it behind nginx, ran a paid task through it and wrote down every number.

What DeepSeek Harness is, and what I tested it on

DeepSeek Harness (the CLI is dsh) is an agent runtime. You give it a model, a working directory and a task. It loops: the model reasons, calls tools, reads files, runs commands and reports back. It ships a web UI, a headless mode, an SDK and an ACP profile for editor integration.

It's MIT licensed. It talks to DeepSeek natively and also to provider ids like anthropic, openai, moonshotai and zai, plus custom OpenAI-compatible or Anthropic Messages endpoints. Each provider speaks one protocol: openai-completions, openai-responses or anthropic-messages. Keys are write-only in the UI and get stored in $DSH_HOME/.credentials.yaml. If you read my piece on self-hosting the DeepSeek V4.1 Flash open weights, this is the other half of the stack.

The repo moves fast: 24,571 commits in 76 days, about 323 a day. It shows 0 open issues, but that's only because bugs go to GitHub Discussions. Already 23,673 public repos carry the dsh-plugin topic.

Test box: 8 vCPU, 23 GiB RAM, Node v26.7.0, npm 11.19.0.

Install: one line and a long wait

$ time npx -y @deepseek-ai/[email protected] --version
0.2.1-rc.2

real    3m16.8s

On a cold npm cache that took 3 minutes 16.8 seconds. Disk cost: 824 MB. node_modules has 193 top-level entries, and the @deepseek-ai scope alone holds 306 packages. I'll come back to why.

One caveat. npm's latest tag is 0.2.1-rc.2 from 27 Oct 2026, but master was already at 0.2.2-alpha.2 hours earlier. Pin the version in anything you script.

$ dsh web --no-open
dsh web: http://127.0.0.1:3080/?token=...

With a fresh DSH_HOME it printed that single line after 8.57 seconds. Right after boot it used 345,132 kB resident (about 337 MB) across 11 threads. It listens on 127.0.0.1:3080 only, never 0.0.0.0. That's the right default.

A plain GET / without the token gets a 401. Following the printed URL with curl -L gets a 200: 37,512 bytes with <title>DeepSeek Harness</title>. After that, a session cookie takes over in the same browser. The token is a process credential. It's printed on every launch and it changes every time.

Running it behind nginx without the 401 trap

The official docs list the proxy requirements, and I checked each one. Keep the Host header as it is and strip the mount prefix. Forward the WebSocket upgrade headers. Rewrite the cookie Path to the mount and add Secure. Redirect a bare /ui to /ui/, because only /ui/ trades the launch token for a session cookie. If nginx is new to you, start with my nginx reverse proxy setup guide. This is the config I'd use:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    server_name dsh.example.com;
    return 301 https://$host$request_uri;
}

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;

    # the bare mount never exchanges the token
    location = /ui {
        return 301 https://$host/ui/$is_args$args;
    }

    location /ui/ {
        # the trailing slash strips /ui/ before the listener sees it
        proxy_pass http://127.0.0.1:3080/;
        proxy_http_version 1.1;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 3600s;
        proxy_buffering off;

        proxy_cookie_path / /ui/;
        proxy_cookie_flags ~ secure;
    }
}

Then start dsh with the public URL and the trusted host:

$ dsh web --no-open --public-url https://dsh.example.com/ui/ --trusted-host dsh.example.com
dsh web: https://dsh.example.com/ui/?token=...

Now the printed URL is the public one. Curling it from the box returns 000, because the name doesn't resolve locally. To test, hit the loopback listener and send the browser's Host header yourself:

curl -s -o /dev/null -w '%{http_code}\n' \
  -H 'Host: dsh.example.com' 'http://127.0.0.1:3080/?token=...'
Request to 127.0.0.1:3080Result
Token, Host: dsh.example.com200, 37,512 bytes
Session cookie, Host: dsh.example.com200
Session cookie, Host: other.example401
Session cookie, Host: 127.0.0.1:3080401
/ui/ with the prefix left on404

Here's the trap. Plenty of proxy configs rewrite Host to the upstream address. If yours does, the UI loads and then every API call returns 401, which looks like a broken app or a bad key. Pass Host through unchanged.

The fence checks the Host header, not who is connecting, so it doesn't protect the port itself. Keep the listener on loopback or firewall 3080. TLS can end at nginx as shown, or on the listener with --tls-cert and --tls-key.

What a headless run actually costs

$ ls
alpha.sh  beta.sh  notes.txt
$ dsh --profile headless "Count the .sh files in this working directory and name them."
...
2 .sh files: alpha.sh, beta.sh.

With a real DeepSeek key it finished in 12 seconds with exit code 0 and printed its reasoning to the terminal. The session log is an append-only, zstd-compressed file, and this one run wrote 28 events to it.

Where the tokens went is the interesting part: 2 API requests, 12,158 input tokens and 113 output tokens. The first request alone was 11,981 input tokens before the agent did anything, because the system prompt and tool schemas ship with every turn. The second was 177.

At DeepSeek's published prices that run cost about 0.19 cents, or $1.89 per thousand runs before any cache discount. Cheap, but look where the tokens went. On small tasks the fixed 12k tokens per turn matter more than the model's price, so trim the tool registry in your profile before you shop for a cheaper model. I've written more on what agent runs actually cost.

The plugin model: what it buys and what it costs

Every part of dsh is a plugin, including the model adapter, the tool registry, the session log and the agent loop itself. There's no privileged core to patch. Plugins register services, typed events and reversible effects on a shared context, built on a framework called Cordis. At boot, each instance builds a plugin tree from ordered layers: the profile's bundles, then the profile's cordis.patch.yml, then the home patch, then any --patch overlays.

That upside is real. You can replace the agent loop without forking, or swap the model adapter for another provider. The shipped web, headless, sdk, sdk-minimal and acp profiles make it easy to drive from scripts and editors. That's the whole case for running your own AI agent: the runtime is yours to read and change.

What it costs you is just as real. That's 306 packages and 824 MB of code you have to trust. Open the profile directory and cordis.yml is literally [], with a comment saying the tree is built from patches, so everything real lives in cordis.patch.yml and the bundles in package.json. Third-party plugins are a supply chain risk too, and I wouldn't load one I hadn't read.

The limits

DeepSeek's own SAFETY.md calls it experimental developer-preview software with no security audit, not to be treated as secure or production-ready. It can run model-generated code and commands, load third-party plugins, and touch files, credentials, processes and the network. Sandboxing and approvals reduce risk but don't guarantee isolation. Here's what else I'd flag:

  • Breaking changes are normal. The npm rc and the alpha on master had already drifted apart.
  • There's no official container image that I could find, only npm or source. If you containerize it yourself, read why a container is not a sandbox first.
  • The launch token travels in the URL, so it ends up in terminal scrollback and in proxy access logs.
  • My session log recorded sandbox mode workspace-write, permission preset workspace-write and approval policy ask. The agent can write to your workspace by default.
  • That ask policy is the only thing between the model and your files. Turn it off and nothing else catches a bad command.

DeepSeek's advice matches mine: least privilege, a disposable VM or container, and read a plugin before you load it.

Who should run it

Run it if you want a provider-agnostic, MIT, self-hosted runtime with a real web UI, or if you're automating against the SDK or ACP. Skip it if you want a finished product today. Don't put it on the internet without a proxy and a firewall. And if you can't keep up with weekly breaking changes, wait.

My daily driver is still Claude Code. I keep dsh as a second runtime for work where I want to swap providers without changing my tooling. Whatever runtime you pick, prove it on a job that matters before you trust it with one.

Frequently asked questions

Is DeepSeek Harness free to self-host?

Yes. It's MIT licensed and installs from npm. You pay for the server and the model tokens. My small test task cost about 0.19 cents.

Can I run DeepSeek Harness behind nginx with a domain?

Yes. Start it with --public-url and --trusted-host. In nginx keep Host unchanged, strip the mount and pass the WebSocket headers. Rewrite the cookie path with Secure and redirect /ui to /ui/. That config handles all of it.

Does DeepSeek Harness work with OpenAI or Claude models?

Yes. Besides DeepSeek, it supports the openai and anthropic provider ids, plus custom OpenAI-compatible or Anthropic Messages endpoints.

Written by Hitesh Saini. I drafted this article with AI assistance, and every number in it was measured on my own server (8 vCPU, 23 GiB RAM) in one session.

If you'd rather have this deployed for you, with TLS, a firewall and approval settings you can live with, that's the kind of job I take on. Have a look at my self-hosting services and send me the details.