Back to home@RaulLazaro

dsh-server-setup

Production-tested setup for running DeepSeek Harness on a VPS with reverse proxy, systemd, and optional Pangolin tunnel

Stars
1
Language
JavaScript
Created
Sep 2, 2026
Updated
Sep 3, 2026

Introduction

DSH Server Setup

Run DeepSeek Harness on a remote VPS with reverse proxy as a DSH plugin.

This is a production-tested setup for running DSH on an Ubuntu server (ARM64 or x64) with:

  • systemd service — auto-restart, logging, persistence
  • Reverse proxy pluginsmanx/dsh-proxy runs inside DSH, configurable from Settings
  • Basic Auth (optional) — protect your instance from unauthorized access
  • Telegram bridge (optional) — notifications and cron alerts to your phone

Architecture

Browser/Phone
     │
     ▼
DSH (0.0.0.0:3080)        ← dsh-proxy plugin handles external access
     │
     ▼
DSH core (127.0.0.1:3079) ← internal port
     │
     ▼
DeepSeek API / MCP servers / Filesystem

The proxy runs as a DSH plugin — no separate process needed. It starts and stops with dsh web.

Quick Start

1. Install prerequisites

# Node.js (via fnm or nvm)
curl -fsSL https://fnm.vercel.app/install | bash
source ~/.bashrc
fnm install 24

# pnpm
npm install -g pnpm

# DSH
npm install -g @deepseek-ai/dsh

2. Create a web profile

mkdir -p ~/.dsh/profiles/web
cd ~/.dsh/profiles/web

3. Add plugins

# Core (already installed with dsh)
# Reverse proxy
pnpm add github:smanx/dsh-proxy

# Your other plugins
pnpm add dshmarket dsh-cron dsh-mnemon dsh-free-search dsh-config-manager ...

4. Configure the profile

~/.dsh/profiles/web/package.json:

{
  "name": "dsh-profile-web",
  "private": true,
  "dependencies": {
    "@smanx/dsh-proxy": "github:smanx/dsh-proxy",
    "dshmarket": "^1.39.0"
  },
  "dsh": {
    "profile": {
      "bundles": [
        "@deepseek-ai/dsh-base",
        "@deepseek-ai/dsh-web-app",
        "@smanx/dsh-proxy",
        "dshmarket"
      ]
    }
  }
}

~/.dsh/profiles/web/cordis.patch.yml:

# dsh-proxy: reverse proxy for LAN/remote access
- id: dsh-proxy
  name: "@smanx/dsh-proxy"
  config:
    listenPort: 3080
    # username: admin       # Uncomment to enable Basic Auth
    # password: changeme    # Uncomment to enable Basic Auth

5. Install the systemd service

sudo cp systemd/dsh.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable dsh
sudo systemctl start dsh

6. Verify

# Check DSH is running
curl -s http://127.0.0.1:3079/ | head -5

# Check proxy is accessible
curl -s http://YOUR_SERVER_IP:3080/ | head -5

Configuration

Proxy Settings (via UI)

Go to Settings → LAN Proxy in the DSH web GUI to:

  • Start/stop the proxy
  • Change the listen port
  • Set username and password for Basic Auth
  • View connection status

Proxy Settings (via cordis.patch.yml)

- id: dsh-proxy
  name: "@smanx/dsh-proxy"
  config:
    listenPort: 3080        # External port (0.0.0.0)
    username: admin         # Basic Auth username (empty = disabled)
    password: changeme      # Basic Auth password

HTTPS / Remote Access

The proxy does not handle HTTPS. For production, put a TLS terminator in front:

  • Pangolin (recommended) — self-hosted identity-aware VPN + reverse proxy with WireGuard, dashboard, and access control
  • Cloudflare Tunnelcloudflared tunnel --url http://127.0.0.1:3080
  • Caddy — auto HTTPS with reverse_proxy localhost:3080
  • Nginx + Let's Encrypt — standard reverse proxy config

Pangolin Setup

Pangolin (22k+ stars) is a self-hosted tunnel that gives you HTTPS + authentication + WireGuard VPN without opening ports. It runs as Docker containers on the same VPS.

1. Install Pangolin:

# Clone and install
git clone https://github.com/fosrl/pangolin.git ~/pangolin
cd ~/pangolin
bash install.sh

2. Configure (~/pangolin/config/config.yml):

domain: yourdomain.com

# TLS (Let's Encrypt or custom cert)
letsencrypt:
  email: you@yourdomain.com
  useLetsEncrypt: true

# Flask secret (generate with: openssl rand -hex 32)
flask_secret: <random-secret>
jwt_secret: <random-secret>

3. Add DSH resource in Pangolin dashboard:

  1. Open https://yourdomain.com → login
  2. Go to ResourcesNew Resource
  3. Set:
    • Name: dsh
    • Protocol: HTTP
    • Target IP: 127.0.0.1
    • Target Port: 3080
  4. Create a Target (the Gerbil client) and generate a config
  5. On your VPS, add the Gerbil client:
# Add the client config from the Pangolin dashboard
sudo nano /etc/pangolin/client/config.yml
sudo systemctl restart pangolin-client

4. Access DSH:

https://dsh.yourdomain.com

Your DSH instance is now accessible over HTTPS with Pangolin's authentication layer on top.

Advantages over direct proxy:

  • HTTPS with automatic Let's Encrypt certificates
  • Built-in authentication (email-based or SSO)
  • WireGuard VPN option for full network access
  • Access control and audit logs
  • No need to open additional ports

Reverse Proxy Features

The proxy (smanx/dsh-proxy, MIT license) provides:

crypto.randomUUID polyfill

DSH's frontend uses crypto.randomUUID() for RPC IDs, but this API is only available in secure contexts (HTTPS/localhost). When accessing via LAN IP or public URL, the polyfill injects a compatible implementation using getRandomValues().

Loopback trust patch

DSH 0.1.1+ checks location.hostname to determine if the browser is local. Non-loopback hosts get degraded behavior (memory-only mode, no settings). The proxy patches the client JS to treat proxied connections as loopback, enabling full functionality.

WebSocket support

The proxy forwards WebSocket connections for real-time DSH features (streaming, live updates).

Public path whitelist

/manifest.webmanifest, /favicon.svg, and /favicon.ico are served without auth so browsers can fetch PWA metadata without credentials.

Telegram Integration (Optional)

For cron notifications to Telegram, see the cron-telegram-bridge setup:

# Create the bridge script at ~/.dsh/scripts/cron-telegram-bridge.js
# Create systemd service
sudo cp systemd/cron-telegram-bridge.service /etc/systemd/system/
sudo systemctl enable cron-telegram-bridge
sudo systemctl start cron-telegram-bridge

Troubleshooting

DSH won't start

  • Check logs: journalctl -u dsh -f
  • Verify Node.js is in PATH: which node
  • Check DSH home exists: ls ~/.dsh/

Proxy not accessible

  • Check if the plugin loaded: Settings → Plugins → dsh-proxy
  • Check port is open: ss -tlnp | grep 3080
  • Check firewall: sudo ufw allow 3080/tcp

WebSocket not working

  • The proxy must forward Upgrade and Connection headers
  • If behind another proxy (nginx/Caddy), ensure WebSocket is enabled there too

Settings page shows "unavailable in this browser"

  • The loopback patch may not be applied
  • Check the proxy plugin is enabled in Settings → Plugins
  • Clear browser cache and reload

Files

dsh-server-setup/
├── README.md                    # This file
├── PLUGINS.md                   # Plugin stack list
├── run.sh                       # DSH wrapper script
├── systemd/
│   └── dsh.service              # Systemd unit file
└── dsh-proxy/                   # Standalone proxy (alternative)
    └── node/
        ├── index.js
        ├── proxy-core.js
        └── package.json

Plugins

See PLUGINS.md for a full list of installed plugins with descriptions.

Credits

License

MIT