Files
BetterDesk/docker-compose.yml
T
UNITRONIX ee913ea910 fix(docker): serialize split admin bootstrap
Refs #385

Thanks: INSOLVE (Honorary); Marco Jakobs (@jacotec); MyNameisStitch (@MyNameisStitch); Redspin (@playerumpknow)
2026-09-26 02:25:51 +02:00

204 lines
7.3 KiB
YAML

# BetterDesk Console - Docker Compose Setup
# Architecture: Go server (single binary) + Node.js web console
#
# ============================================================================
# QUICK START (no build required):
# curl -fsSL https://raw.githubusercontent.com/UNITRONIX/Rustdesk-FreeConsole/main/docker-compose.quick.yml -o docker-compose.yml
# docker compose up -d
#
# BUILD FROM SOURCE (this file):
# docker compose build && docker compose up -d
# ============================================================================
#
# The Go server replaces separate hbbs/hbbr — it handles signal, relay,
# and HTTP API in a single process.
services:
# BetterDesk Server (Go) — replaces hbbs + hbbr
# Single binary: signal (21116), relay (21117), API (21114), NAT test (21115)
server:
build:
context: .
dockerfile: Dockerfile.server
image: betterdesk-server:local
pull_policy: never
container_name: betterdesk-server
hostname: betterdesk-server
command: ["/usr/local/bin/betterdesk-server", "-mode", "all", "-api-port", "21114", "-key-file", "/opt/rustdesk/id_ed25519"]
ports:
- "21114:21114" # Go HTTP API (default — direct client + REST)
- "21115:21115" # NAT type test
- "21116:21116/tcp" # Signal TCP
- "21116:21116/udp" # Signal UDP
- "21117:21117" # Relay TCP
- "21118:21118" # WebSocket Signal
- "21119:21119" # WebSocket Relay
volumes:
- rustdesk-data:/opt/rustdesk
- console-data:/app/data:ro
environment:
- ENCRYPTED_ONLY=1
- DB_URL=${DB_URL:-/opt/rustdesk/db_v2.sqlite3}
- AUTH_DB_PATH=/app/data/auth.db
# Admin credentials (first run only; existing users are not overwritten).
# Keep these mappings in both services so Go and Node.js seed the same account.
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-}
- INIT_ADMIN_USER=${ADMIN_USERNAME:-admin}
- INIT_ADMIN_PASS=${ADMIN_PASSWORD:-}
- SQLITE_AUTH_DB_MODE=${SQLITE_AUTH_DB_MODE:-}
- SIGNAL_RATE_LIMIT_PER_IP=${SIGNAL_RATE_LIMIT_PER_IP:-20}
# Opt-in compatibility for stock controller-only clients. Effective
# only with ENROLLMENT_MODE=open; strict initiator authorization remains
# the default.
- ALLOW_LEGACY_OUTBOUND=${ALLOW_LEGACY_OUTBOUND:-N}
- P2P_FIRST=${P2P_FIRST:-Y}
- ALWAYS_USE_RELAY=${ALWAYS_USE_RELAY:-N}
- P2P_FALLBACK_MS=${P2P_FALLBACK_MS:-2000}
- SAME_NAT_RELAY=${SAME_NAT_RELAY:-Y}
# Enrollment policy. Fresh volumes default to "managed" (operator approves
# new devices); pre-existing volumes keep their current mode. Set to
# "open", "managed", or "locked" to override.
- ENROLLMENT_MODE=${ENROLLMENT_MODE:-}
- NTP_SERVERS=${NTP_SERVERS:-pool.ntp.org,time.google.com,time.cloudflare.com}
- BILLING_MAX_CLOCK_SKEW_MS=${BILLING_MAX_CLOCK_SKEW_MS:-2000}
- BILLING_REQUIRE_SYNCED_CLOCK=${BILLING_REQUIRE_SYNCED_CLOCK:-1}
- BILLING_TRUST_OS_NTP=${BILLING_TRUST_OS_NTP:-Y}
# Optional: map betterdesk to host bind-mount owner (Synology etc.). Default 10001.
- PUID=${PUID:-10001}
- PGID=${PGID:-10001}
networks:
- betterdesk-net
restart: unless-stopped
# SECURITY (audit fix L-02, 2026-04-10): drop all capabilities except
# those needed by the entrypoint to chown volumes + su-exec to non-root.
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- SETUID
- SETGID
- CHOWN
- FOWNER
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:21114/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
# BetterDesk Console — Node.js Web Interface
console:
build:
context: .
dockerfile: Dockerfile.console
image: betterdesk-console:local
pull_policy: never
container_name: betterdesk-console
hostname: betterdesk-console
ports:
- "5000:5000" # Web console (admin panel)
- "21121:21121" # Backward compat proxy → Go :21114
volumes:
- rustdesk-data:/opt/rustdesk # Shared server data (keys, db) — needs write for WAL mode
- console-data:/app/data # Console sessions and optional legacy auth.db
environment:
- NODE_ENV=production
# Set TRUST_PROXY=1 when this console is behind one reverse proxy.
- TRUST_PROXY=${TRUST_PROXY:-}
- PORT=5000
- HOST=0.0.0.0
- API_HOST=0.0.0.0
- API_ENABLED=true
- API_PORT=21121
- RUSTDESK_API_PROXY=true
- GO_API_PORT=21114
- SERVER_BACKEND=betterdesk
- HBBS_API_URL=http://betterdesk-server:21114/api
- BETTERDESK_API_URL=http://betterdesk-server:21114/api
- RUSTDESK_PATH=/opt/rustdesk
- DATA_DIR=/app/data
- BETTERDESK_CLIENT_REPO=${BETTERDESK_CLIENT_REPO:-UNITRONIX/BetterDesk-Client}
- BETTERDESK_CLIENT_RELEASE_TAG=${BETTERDESK_CLIENT_RELEASE_TAG:-}
- BETTERDESK_GITHUB_TOKEN=${BETTERDESK_GITHUB_TOKEN:-}
- BETTERDESK_CUSTOM_CLIENT_SIGNING_SEED=${BETTERDESK_CUSTOM_CLIENT_SIGNING_SEED:-}
- AGENT_ARTIFACT_DIR=/app/data/agent-builds
- AGENT_BUILD_WORKER=${AGENT_BUILD_WORKER:-on}
- AGENT_BUILD_POLL_MS=${AGENT_BUILD_POLL_MS:-5000}
- AGENT_BUILD_COOLDOWN_MS=${AGENT_BUILD_COOLDOWN_MS:-1000}
- DB_PATH=/opt/rustdesk/db_v2.sqlite3
- SQLITE_AUTH_DB_MODE=${SQLITE_AUTH_DB_MODE:-}
- PUB_KEY_PATH=/opt/rustdesk/id_ed25519.pub
- API_KEY_PATH=/opt/rustdesk/.api_key
- SESSION_SECRET=${SESSION_SECRET:-}
# Admin credentials (first run only; existing users are not overwritten).
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-}
- DEFAULT_ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
- DEFAULT_ADMIN_PASSWORD=${ADMIN_PASSWORD:-}
- WS_HBBS_HOST=betterdesk-server
- WS_HBBS_PORT=21116
- WS_HBBR_HOST=betterdesk-server
- WS_HBBR_PORT=21117
- DOCKER=true
# Optional: map betterdesk to host bind-mount owner (Synology etc.). Default 10001.
- PUID=${PUID:-10001}
- PGID=${PGID:-10001}
networks:
- betterdesk-net
restart: unless-stopped
# SECURITY (audit fix L-02, 2026-04-10): drop all caps except those
# needed by su-exec (SETUID/SETGID) and volume permission fixes (CHOWN/FOWNER).
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- SETUID
- SETGID
- CHOWN
- FOWNER
depends_on:
server:
condition: service_started
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:5000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
# Nginx Reverse Proxy (Optional — only with --profile nginx)
nginx:
image: nginx:alpine
container_name: betterdesk-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
networks:
- betterdesk-net
restart: unless-stopped
depends_on:
- console
profiles:
- nginx
volumes:
rustdesk-data:
driver: local
driver_opts:
type: none
o: bind
device: ${RUSTDESK_DATA_PATH:-./data}
console-data:
driver: local
networks:
betterdesk-net:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16