9.6 KiB
RustDesk Server — Docker Compose Deployment
The repo ships a docker-compose.yml that builds the server from source
(this fork's pro-features branch) and runs hbbs + hbbr as two
containers. No prebuilt image is pulled — every docker compose build
clones the configured Git URL and runs cargo build --release inside the
build stage.
For the runtime flag reference (CLI options accepted by hbbs itself), see
CONFIGURATION.md. This document only covers the Compose
glue.
Quick start
cp .env.example .env
# edit .env — at minimum set RUSTDESK_DOMAIN, and change
# RUSTDESK_BOOTSTRAP_ADMIN_PASSWORD before the first boot
docker compose up -d --build
The bootstrap admin (default admin / changeme) is seeded into the
users table on the first boot only — once the row exists, those
flags are ignored. If you boot with the default password and forget to
change it, rotate it via the admin UI; if you forget the password
entirely, delete ./data/db_v2.sqlite3 (loses all server-side state) or
edit the users row with sqlite3 directly.
First build pulls the Rust toolchain image and compiles the workspace; expect several minutes. Subsequent builds reuse the cargo cache layer unless the Git ref or build args change.
After it boots:
| Endpoint | Port | Purpose |
|---|---|---|
tcp://<domain>:21115 |
21115 | NAT test |
tcp+udp://<domain>:21116 |
21116 | ID / rendezvous (desktop clients) |
tcp://<domain>:21117 |
21117 | Relay (hbbr) |
ws://<domain>:21118 |
21118 | Browser-facing rendezvous WebSocket |
ws://<domain>:21119 |
21119 | Browser-facing relay WebSocket |
http://<domain>:21114/admin/ |
21114 | Admin dashboard (pro-features) |
http://<domain>:21114/api/* |
21114 | Management API (pro-features) |
Persistent state — including the auto-generated id_ed25519 keypair and the
SQLite database — lives in ./data/ (bind-mounted to /root in both
containers).
Files
| File | Role |
|---|---|
docker-compose.yml |
Two services (hbbs, hbbr) sharing one image built from docker/Dockerfile.source. |
docker/Dockerfile.source |
Multi-stage build: clones the repo, runs cargo build --release, copies binaries into a debian:bookworm-slim runtime. |
.env.example |
Documented template; copy to .env. |
data/ |
Created on first run. Contains keypair + SQLite DB. Back this up. |
The legacy single-stage docker/Dockerfile (busybox + s6-overlay, expects
prebuilt binaries) and docker-classic/Dockerfile are unrelated to this
flow and unused by docker-compose.yml.
Environment variables
These are read by docker-compose.yml from .env. Compose ships them
through to the container as command-line flags or environment: entries,
not as raw process env (with the exception of RUST_LOG and
ALWAYS_USE_RELAY, which hbbs reads from env directly).
Runtime
| Variable | Default | Effect |
|---|---|---|
RUSTDESK_DOMAIN |
required | Public hostname clients connect to. Passed as hbbs -r ${RUSTDESK_DOMAIN}:21117. |
RUSTDESK_BOOTSTRAP_ADMIN_USERNAME |
admin |
Seeded as the initial admin on first boot only (when the users table is empty). Ignored on subsequent restarts. Empty disables the bootstrap. |
RUSTDESK_BOOTSTRAP_ADMIN_PASSWORD |
changeme |
Same — bcrypt-hashed at insert. Change this in .env before the first up, or rotate via the admin UI immediately after. |
RUSTDESK_KEY |
- |
Pre-shared key. - = auto-generate on first boot (written to ./data/id_ed25519{,.pub}); _ = encrypted-only with auto-key; or paste a base64 public key to pin it. Applied to both hbbs and hbbr via -k. |
RUSTDESK_HTTP_PORT |
21114 |
Pro-features admin API + dashboard port. Set to 0 to disable HTTP entirely. The host port published is the same value. |
RUSTDESK_ALWAYS_USE_RELAY |
N |
Force every session through the relay even on LAN. Read from env by hbbs (any non-empty/non-N value enables). |
RUST_LOG |
info |
Log filter. e.g. debug, hbbs=debug,sqlx=warn. |
Build source
| Variable | Default | Effect |
|---|---|---|
RUSTDESK_GIT_URL |
https://gitea.cstudio.ch/mike/rustdesk-server.git |
Repo cloned inside the builder stage. |
RUSTDESK_GIT_BRANCH |
pro-features |
Branch / tag / commit to check out (--branch so it must be a ref name, not a SHA). |
DATABASE_URL |
unset (uses the cloned repo's .env) |
Overrides the DATABASE_URL sqlx reads at compile time. Rarely needed — see below. |
Build-arg changes only take effect when the image is rebuilt:
docker compose build --no-cache hbbs (or up -d --build).
Why DATABASE_URL is a build-time concern
hbbs uses sqlx::query! macros, which verify SQL at compile time by
running the queries against a real SQLite database. The repo includes a
checked-in db_v2.sqlite3 with the schema pre-applied, and a tracked .env
file pointing at it (DATABASE_URL=sqlite://./db_v2.sqlite3).
Cargo automatically reads .env from the project root, so cargo build
inside the builder stage Just Works without any explicit configuration.
You only need to set DATABASE_URL in the Compose .env if you fork the
schema or want to point the compile-time check at a different SQLite file.
At runtime the binary opens its own DB under /root/ (your ./data/
bind mount) — that path is not configurable via this variable.
Adding extra hbbs flags
docker-compose.yml only wires the most common flags. To pass others
(SMTP, OIDC, recording dir, audit retention, …), edit the command:
block of the hbbs service. Example — enable SMTP and set the public
base URL needed for OIDC callbacks:
command: >
hbbs
-r ${RUSTDESK_DOMAIN:?...}:21117
-k ${RUSTDESK_KEY:--}
--http-port ${RUSTDESK_HTTP_PORT:-21114}
--admin-ui-dir /opt/rustdesk/admin_ui
--bootstrap-admin-username=${RUSTDESK_BOOTSTRAP_ADMIN_USERNAME:-}
--bootstrap-admin-password=${RUSTDESK_BOOTSTRAP_ADMIN_PASSWORD:-}
--public-base-url https://${RUSTDESK_DOMAIN}:${RUSTDESK_HTTP_PORT:-21114}
--smtp-host ${SMTP_HOST}
--smtp-user ${SMTP_USER}
--smtp-pass ${SMTP_PASS}
--smtp-from ${SMTP_FROM}
Then add the matching variables to .env. The full flag list lives in
CONFIGURATION.md.
If you mount an oidc.toml, drop it into ./data/ and pass
--oidc-config /root/oidc.toml.
Operational notes
Upgrading. Pull the latest commit on pro-features and rebuild:
docker compose build --pull --no-cache
docker compose up -d
The --pull refreshes the Rust toolchain base image; --no-cache forces a
fresh git clone (otherwise Docker will reuse the cached clone layer).
Alternatively, bump RUSTDESK_GIT_BRANCH to a tag and rebuild — the changed
build arg invalidates the clone layer automatically.
Logs. docker compose logs -f hbbs (or hbbr). Both containers run
the binary in the foreground.
Persistence. Everything that matters is in ./data/:
id_ed25519{,.pub} (the server keypair — losing this invalidates every
existing client) and db_v2.sqlite3 (users, address books, audit, etc.).
Back up the whole directory.
TLS. The server itself speaks plain HTTP on 21114 and plain WebSocket
on 21118 / 21119. Front it with nginx or Caddy for TLS — see the "TLS
deployment" section in CONFIGURATION.md. When you do, set
--http-listen=127.0.0.1 and --ws-listen=127.0.0.1 in the command:
block so the reverse proxy can claim the public ports.
Building behind a proxy. Pass HTTP_PROXY / HTTPS_PROXY build args
through Compose:
build:
<<: *rustdesk-build
args:
HTTP_PROXY: http://proxy.internal:3128
HTTPS_PROXY: http://proxy.internal:3128
Resource hint. Cold compile takes ~3–5 GB of RAM for the linker step
(LTO + codegen-units = 1). On a small VPS, build the image on a beefier
machine, push to a registry, and pull from the VPS instead — set the
image: field to a registry tag and drop the build: block.