-
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.
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 |
false |
Enables the in-browser client. |
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
}
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>.