-
Notifications
You must be signed in to change notification settings - Fork 4
Install with Docker
The image is self-contained: php-fpm, nginx, the scheduler, the native web client, and a WebSocket bridge to your RustDesk server. It listens on 8080.
Images are published on every release:
docker pull marcpope/cortendesk:0.9.0
docker pull ghcr.io/marcpope/cortendesk:0.9.0latest and the <major>.<minor> tag (e.g. 0.9) move only on stable
releases, never on a pre-release.
You need a running open-source RustDesk server (hbbs/hbbr) and three values
from it:
| Value | Where it comes from |
|---|---|
| ID server | hbbs.example.com:21116 |
| Relay server | hbbs.example.com:21117 |
| Public key | contents of id_ed25519.pub on the server |
CortenDesk does not replace hbbs/hbbr. It runs alongside them and speaks the same HTTP API the RustDesk clients already use.
Do not have one yet, or not sure yours is set up correctly? RustDesk server in Docker builds both services from scratch and covers the three things that usually go wrong: the key, UDP 21116, and an unreachable relay.
docker run -d --name cortendesk \
-p 8080:8080 \
-v cortendesk-data:/data \
-e CORTENDESK_ID_SERVER=hbbs.example.com:21116 \
-e CORTENDESK_RELAY_SERVER=hbbs.example.com:21117 \
-e CORTENDESK_PUBLIC_KEY="<contents of id_ed25519.pub>" \
marcpope/cortendesk:0.9.0Open http://localhost:8080 and sign in as admin / changeme. Change that password immediately.
Good enough to evaluate. For anything real, use MySQL.
services:
cortendesk:
image: marcpope/cortendesk:0.9.0
ports:
- "8080:8080"
environment:
APP_URL: https://console.example.com
SESSION_SECURE_COOKIE: "true"
DB_CONNECTION: mysql
DB_HOST: db
DB_DATABASE: cortendesk
DB_USERNAME: cortendesk
DB_PASSWORD: use-a-real-password
CORTENDESK_ID_SERVER: hbbs.example.com:21116
CORTENDESK_RELAY_SERVER: hbbs.example.com:21117
CORTENDESK_PUBLIC_KEY: "<contents of id_ed25519.pub>"
# Web client endpoints as the BROWSER sees them (your TLS proxy):
CORTENDESK_NATIVE_WEBCLIENT: "true"
CORTENDESK_WS_ID_URL: wss://console.example.com/ws/id
CORTENDESK_WS_RELAY_URL: wss://console.example.com/ws/relay
volumes:
- cortendesk-data:/data
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: mysql:8.4
environment:
MYSQL_DATABASE: cortendesk
MYSQL_USER: cortendesk
MYSQL_PASSWORD: use-a-real-password
MYSQL_RANDOM_ROOT_PASSWORD: "1"
volumes:
- cortendesk-db:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1"]
interval: 5s
timeout: 3s
retries: 20
restart: unless-stopped
volumes:
cortendesk-data:
cortendesk-db:docker compose up -d
docker compose logs -f cortendesk| Variable | Default | Notes |
|---|---|---|
APP_URL |
http://localhost:8080 |
Your public URL. Generated links and the OIDC callback depend on it. |
APP_KEY |
generated | Left unset, one is generated on first boot and kept in /data/.app_key. Keep that volume. |
SESSION_SECURE_COOKIE |
false |
Set true when served over HTTPS. |
DB_CONNECTION |
sqlite |
mysql for anything real. |
DB_HOST / DB_DATABASE / DB_USERNAME / DB_PASSWORD
|
— | Standard Laravel. |
CORTENDESK_ID_SERVER |
— |
host:21116. Also the default host for the ws bridge. |
CORTENDESK_RELAY_SERVER |
— |
host:21117. |
CORTENDESK_PUBLIC_KEY |
— | Contents of id_ed25519.pub. |
CORTENDESK_NATIVE_WEBCLIENT |
true |
Enables the in-browser client. Set false to hide it. |
CORTENDESK_WS_ID_URL / CORTENDESK_WS_RELAY_URL
|
— |
wss:// URLs your proxy exposes. Browsers refuse ws:// from an HTTPS page. |
RUSTDESK_WS_HOST |
host of CORTENDESK_ID_SERVER
|
Override only if hbbs/hbbr are reachable at a different address from inside the container. |
CORTENDESK_ADMIN_USER |
admin |
First-boot account only. |
CORTENDESK_ADMIN_PASSWORD |
changeme |
First-boot account only. |
TRUSTED_PROXIES |
private ranges | Override if your proxy sits outside RFC1918. |
Most settings can also be changed later in Settings without restarting.
- Generates
APP_KEYinto/dataif you did not supply one. - Waits for the database, then runs migrations.
- Creates the admin account — a no-op once any user exists.
- Renders the nginx config, pointing
/ws/idand/ws/relayat your RustDesk server. - Starts php-fpm, nginx, and the Laravel scheduler.
Migrations run on every start, so upgrading is just a new image tag.
Put a TLS-terminating proxy in front (Traefik, Caddy, nginx, Cloudflare). The
app honours X-Forwarded-*, so no extra configuration is needed beyond setting
APP_URL and SESSION_SECURE_COOKIE=true.
The in-browser client needs wss://. The container already bridges /ws/id and
/ws/relay to hbbs/hbbr — your proxy just has to forward those two paths with
WebSocket upgrade headers, and CORTENDESK_WS_*_URL must be the wss:// URLs
as the browser sees them, not the container's internal address.
Caddy, which needs nothing else:
console.example.com {
reverse_proxy localhost:8080
}
Traefik labels, an nginx server block, and what TRUSTED_PROXIES is actually
for: Reverse proxy and TLS.
On each RustDesk client, Settings → Network: set ID Server, Relay Server, Key, and API Server to your console URL. Devices appear within one heartbeat (about fifteen seconds). The console's Settings screen shows copy-paste values for all four.
For unattended deployment, use an API token and the client's --assign flag.
docker compose pull
docker compose up -dMigrations run automatically on start. Back up the database first — see How to Upgrade.
Two things matter:
- The database — everything except the app key.
-
/data— holds the generatedAPP_KEY. Lose it and encrypted values (SMTP password, OIDC client secret, 2FA secrets) cannot be decrypted, even with a good database backup.
docker compose exec db mysqldump -u cortendesk -p cortendesk > cortendesk.sql
docker run --rm -v cortendesk-data:/data -v "$PWD":/backup alpine \
tar czf /backup/cortendesk-data.tar.gz /dataNo devices appear. The clients' API Server is not set to the console, or they cannot reach it. Check Logs → Connections and the container log.
Web client will not connect. CORTENDESK_WS_*_URL must be wss:// and must
be reachable from the browser. Check the browser console for a blocked
mixed-content or upgrade error.
"Please provide a valid cache path" or permission errors on start. The
/data volume is not writable. Recreate it rather than chowning by hand.
Sign-in redirects back to the login page over HTTPS. Set
SESSION_SECURE_COOKIE=true and make sure APP_URL uses https://.
Locked out of SSO or 2FA. Break-glass paths exist:
CORTENDESK_OIDC_DISABLED=true in the environment, and
docker compose exec cortendesk php artisan cortendesk:2fa-reset <username>.