Files
UNITRONIX 59e14f7576 fix(mesh): PEM web cert hash and MESH_WEB_CERT_FILE for reverse proxy (Fixes #321)
MeshAgent behind TLS-terminating proxies was rejected with bad web cert hash when Go hashed TLS_CERT incorrectly (raw PEM) or used an internal cert instead of the public LE cert agents see.
2026-07-29 19:15:23 +02:00

13 KiB

BetterDesk Go Server — Deployment Guide

Overview

BetterDesk Go Server is a single binary that replaces the Rust hbbs (signal) + hbbr (relay) servers. It is fully compatible with original RustDesk clients and provides additional features:

  • Combined service: Signal + Relay + HTTP API + Admin TCP in one process
  • Enhanced device tracking: Online/Offline/Degraded/Critical status with configurable timeouts
  • Security: JWT auth, TOTP 2FA, RBAC, API keys, rate limiting, connection limiting
  • Observability: Prometheus metrics, structured JSON logging, audit trail
  • WebSocket support: Signal and Relay over WebSocket for restricted networks

Prerequisites

  • Linux x86_64
  • Root/sudo access
  • Ports 21114-21119 + 21000 available
  • Existing RustDesk/BetterDesk installation (optional, for migration)

Quick Deployment

1. Upload files to server

# From your local machine, upload the deployment files
scp betterdesk-server-linux-amd64 user@server:/tmp/
scp tools/migrate/migrate-linux-amd64 user@server:/tmp/
scp deploy.sh user@server:/tmp/

2. Run the deployment script

ssh user@server

# Make executable
chmod +x /tmp/deploy.sh

# Interactive deployment (recommended for first time)
sudo /tmp/deploy.sh

# OR automatic deployment with defaults
sudo /tmp/deploy.sh --auto

3. Verify

# Check service status
systemctl status betterdesk

# Check API health
curl http://localhost:21114/api/health

# Check logs
journalctl -u betterdesk -f

# Get initial admin credentials (printed at first start)
journalctl -u betterdesk | grep "INITIAL ADMIN"

Manual Deployment

Step 1: Backup existing database

# Create backup directory
mkdir -p /opt/betterdesk/backups

# Backup existing database (adjust path to your installation)
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
cp /opt/rustdesk/db_v2.sqlite3 /opt/betterdesk/backups/db_v2.sqlite3.${TIMESTAMP}
cp /opt/rustdesk/db_v2.sqlite3-wal /opt/betterdesk/backups/db_v2.sqlite3-wal.${TIMESTAMP} 2>/dev/null
cp /opt/rustdesk/db_v2.sqlite3-shm /opt/betterdesk/backups/db_v2.sqlite3-shm.${TIMESTAMP} 2>/dev/null

# Backup key files
cp /opt/rustdesk/id_ed25519 /opt/betterdesk/backups/id_ed25519.${TIMESTAMP}
cp /opt/rustdesk/id_ed25519.pub /opt/betterdesk/backups/id_ed25519.pub.${TIMESTAMP}

Step 2: Migrate database

# Run migration tool
./migrate-linux-amd64 -src /opt/rustdesk/db_v2.sqlite3 -dst /opt/betterdesk/data/db_v2.sqlite3

Step 3: Stop old services

sudo systemctl stop rustdesksignal rustdeskrelay
sudo systemctl disable rustdesksignal rustdeskrelay

Step 4: Install binary

sudo mkdir -p /opt/betterdesk/data /var/log/betterdesk
sudo cp betterdesk-server-linux-amd64 /opt/betterdesk/betterdesk-server
sudo chmod +x /opt/betterdesk/betterdesk-server

# Copy existing key file (important for client compatibility!)
sudo cp /opt/rustdesk/id_ed25519 /opt/betterdesk/data/
sudo cp /opt/rustdesk/id_ed25519.pub /opt/betterdesk/data/

Step 5: Create systemd service

sudo cat > /etc/systemd/system/betterdesk.service << 'EOF'
[Unit]
Description=BetterDesk Server (Signal + Relay + API)
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/opt/betterdesk/data
ExecStart=/opt/betterdesk/betterdesk-server \
    -mode all \
    -db /opt/betterdesk/data/db_v2.sqlite3 \
    -key-file /opt/betterdesk/data/id_ed25519 \
    -port 21116 \
    -relay-port 21117 \
    -api-port 21114 \
    -admin-port 21000 \
    -log-format json \
    -audit-log /var/log/betterdesk/audit.jsonl
Restart=always
RestartSec=5
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable betterdesk
sudo systemctl start betterdesk

Step 6: Open firewall ports

# UFW
sudo ufw allow 21114:21119/tcp
sudo ufw allow 21116/udp

# OR firewalld
sudo firewall-cmd --permanent --add-port=21114-21119/tcp
sudo firewall-cmd --permanent --add-port=21116/udp
sudo firewall-cmd --reload

Port Reference

Port Protocol Service Description
21114 TCP HTTP API REST API, health checks
21115 TCP NAT Test NAT type detection
21116 UDP+TCP Signal Server Client registration, ID lookup
21117 TCP Relay Server Connection relay
21118 TCP WebSocket Signal Signal over WS (web clients)
21119 TCP WebSocket Relay Relay over WS (restricted networks)
21000 TCP Admin TCP Admin command interface (LAN only)

Configuration

Command-line flags

-mode string        Server mode: all, signal, relay (default "all")
-port int           Signal server port (default 21116)
-relay-port int     Relay server port (default 21117)
-api-port int       HTTP API port (default 21114)
-admin-port int     Admin TCP port (default 21000), 0 to disable
-db string          SQLite database path (default "db_v2.sqlite3")
-key-file string    Ed25519 key file path (default "id_ed25519")
-relay string       External relay address (host:port)
-log-format string  Log format: text, json (default "text")
-log-level string   Log level: debug, info, warn, error (default "info")
-audit-log string   Audit log file path (default "" = disabled)

Environment variables

Variable Default Description
JWT_SECRET auto JWT signing secret
ADMIN_PASSWORD auto Initial admin password
FORCE_HTTPS N Require HTTPS for API
RELAY_MAX_CONNS_PER_IP 20 Max relay connections per IP
SIGNAL_RATE_LIMIT_PER_IP 20 Max signal registrations per proxy/client bucket per minute (0 disables)
TLS_CERT - TLS certificate path
TLS_KEY - TLS private key path
MESH_WEB_CERT_FILE - Public TLS cert for MeshAgent web-hash (overrides TLS_CERT for mesh only)
PEER_TIMEOUT_SECS 15 Seconds before peer marked offline
TRUST_PROXY N Trust X-Forwarded-For / X-Real-IP (requires TRUSTED_PROXIES)
TRUSTED_PROXIES (empty) Comma-separated CIDR/IP allowlist of reverse proxies; empty = ignore forwarded headers
NTP_SERVERS public pools Comma-separated NTP hostnames/IPs for billing clock checks
BILLING_MAX_CLOCK_SKEW_MS 2000 Max allowed clock offset vs NTP (ms)
BILLING_REQUIRE_SYNCED_CLOCK Y (Linux default) Block billable sessions when clock unsynced
BILLING_TRUST_OS_NTP Y on Linux, N elsewhere Trust timedatectl when direct NTP queries fail

NTP/billing variables are stored in the web console .env on native installs. Linux betterdesk-server.service loads that file via EnvironmentFile. After changing NTP settings, restart betterdesk-server (not only the console). The panel path: Commercialization → Settings → Server time (NTP).

For NGINX stream or other UDP/TCP reverse proxy deployments, remember that signal traffic on 21116 is not HTTP and cannot carry X-Forwarded-For. TRUST_PROXY only applies to HTTP/API handlers. If many legitimate devices share the same proxy address, raise SIGNAL_RATE_LIMIT_PER_IP or set it to 0 only on trusted private networks.

Migration from Rust (hbbs-patch-v2)

What gets migrated

The migration tool converts data from the Rust peer table to the Go peers table:

Rust Column Go Column Transformation
guid uuid BLOB → hex string
id id (PK) Used as primary key
info JSON hostname, os, version JSON parsed into columns
status status 0→OFFLINE, 1→ONLINE
user user BLOB → string
is_banned banned 1→true, 0/NULL→false
is_deleted soft_deleted 1→true, 0/NULL→false
last_online last_online Preserved as-is

Key file compatibility

IMPORTANT: The Ed25519 key file (id_ed25519) from the Rust server is fully compatible. Copy it to the new installation to keep your existing clients connected without reconfiguration.

Backup-only mode

# Only create a backup without installing anything
./migrate-linux-amd64 -src /opt/rustdesk/db_v2.sqlite3 -backup-only

Rollback

If something goes wrong, you can revert to the Rust server:

Using the deploy script

sudo /path/to/deploy.sh --rollback

Manual rollback

# Stop Go server
sudo systemctl stop betterdesk
sudo systemctl disable betterdesk

# Restore backup
cp /opt/betterdesk/backups/backup_YYYYMMDD_HHMMSS/db_v2.sqlite3 /opt/rustdesk/
cp /opt/betterdesk/backups/backup_YYYYMMDD_HHMMSS/id_ed25519 /opt/rustdesk/

# Re-enable old services
sudo systemctl enable rustdesksignal rustdeskrelay
sudo systemctl start rustdesksignal rustdeskrelay

Troubleshooting

Service won't start

# Check logs
journalctl -u betterdesk -n 100 --no-pager

# Common issues:
# - Port already in use → check if old hbbs/hbbr is still running
# - Permission denied → ensure /opt/betterdesk/data is writable
# - Key file not found → check -key-file path

Clients can't connect

  1. Check ports are open: ss -tlnp | grep 2111
  2. Check firewall: ufw status or firewall-cmd --list-all
  3. Check key file: Clients store the server public key. If the key changed, clients need reconfiguration
  4. Check DNS/IP: Ensure clients point to the correct server address

Devices show offline

  1. Check API: curl http://localhost:21114/api/peers
  2. Check signal logs: journalctl -u betterdesk | grep RegisterPeer
  3. Increase timeout: Set PEER_TIMEOUT_SECS=30 in service environment

Performance tuning

# Increase file descriptor limit in service
LimitNOFILE=131072

# Increase relay bandwidth
# (set via API or admin TCP)

API Quick Reference

# Health check
curl http://localhost:21114/api/health

# List all peers
curl http://localhost:21114/api/peers

# Server statistics
curl http://localhost:21114/api/server/stats

# Peer statistics
curl http://localhost:21114/api/peers/stats

# Login (get JWT token)
curl -X POST http://localhost:21114/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"YOUR_PASSWORD"}'

# Use JWT token
curl http://localhost:21114/api/peers \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Architecture

                          ┌─────────────────────────┐
                          │   betterdesk-server      │
                          │   (single binary)        │
  ┌───────────────────────┼─────────────────────────┤
  │                       │                         │
  │  ┌──────────┐  ┌──────────┐  ┌──────────┐     │
  │  │  Signal   │  │  Relay   │  │ HTTP API │     │
  │  │  Server   │  │  Server  │  │  Server  │     │
  │  │          │  │          │  │          │     │
  │  │ UDP:21116│  │ TCP:21117│  │ TCP:21114│     │
  │  │ TCP:21116│  │ WS:21119 │  │          │     │
  │  │ WS:21118 │  │          │  │          │     │
  │  │ NAT:21115│  │          │  │          │     │
  │  └──────────┘  └──────────┘  └──────────┘     │
  │       │              │              │           │
  │  ┌────┴──────────────┴──────────────┴──────┐   │
  │  │              SQLite Database             │   │
  │  │         /opt/betterdesk/data/            │   │
  │  │         db_v2.sqlite3                    │   │
  │  └─────────────────────────────────────────┘   │
  │                                                 │
  │  ┌──────────┐  ┌──────────┐  ┌──────────┐     │
  │  │  Auth    │  │  Audit   │  │ Metrics  │     │
  │  │  JWT/2FA │  │  Events  │  │Prometheus│     │
  │  └──────────┘  └──────────┘  └──────────┘     │
  └─────────────────────────────────────────────────┘