Repository navigation
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
220 lines (214 loc) · 8.23 KB
/
Copy pathdocker-compose.yml
File metadata and controls
220 lines (214 loc) · 8.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# BetterDesk Console - Docker Compose Setup (source build / local images)
# Architecture: Go server (single binary) + Node.js web console
#
# ============================================================================
# THIS FILE builds images locally (betterdesk-*:local, pull_policy: never).
# `docker compose pull` does NOT refresh BetterDesk — it never pulls *:local.
#
# Update path (source build):
# git pull
# docker compose up -d --build
# # or: docker compose build --no-cache && docker compose up -d --force-recreate
#
# PREBUILT GHCR (no local build) — use instead:
# curl -fsSL https://raw.githubusercontent.com/UNITRONIX/BetterDesk/main/docker-compose.quick.yml -o docker-compose.yml
# docker compose pull && docker compose up -d
# Prefer the official all-in-one image: docker-compose.quick.single.yml
#
# Port map (split layout):
# :5000 — web panel (Node)
# :21114 — Go Client API (RustDesk /api/login, AB) — preferred for new clients
# :21121 — Node → Go compat proxy (legacy client API URLs)
# :21118 / :21119 — native RustDesk WSS signal / relay
# ============================================================================
#
# 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}
# Web Remote (#465 / #313): console PunchHole arrives from the Docker
# bridge IP (not loopback). Default trusts compose 172.16/12 + loopback.
# On a shared Docker host, override to the console container /32.
- PANEL_SIGNAL_PROXY_CIDRS=${PANEL_SIGNAL_PROXY_CIDRS:-172.16.0.0/12,127.0.0.0/8,::1/128}
- 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