CortenDesk

Install

Up and running

CortenDesk is a console for a RustDesk server you already run. If you do not have one yet, set that up first — the wiki covers it.


Step 1

Collect three values

All three come from your RustDesk server.

ValueLooks likeWhere it comes from
ID serverhbbs.example.com:21116Your hbbs host and port
Relay serverhbbs.example.com:21117Your hbbr host and port
Public keybase64…Contents of id_ed25519.pub on the server
Paste the key with no surrounding whitespace. It is compared exactly, so a trailing newline picked up from an editor fails in a way that looks identical to a wrong key — clients simply will not connect.

Step 2 · quick trial

One container, SQLite

Enough to evaluate. No database to set up.

# Pull and run
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>" \
  ghcr.io/marcpope/cortendesk

Open http://localhost:8080 and sign in as admin / changeme. Change that password immediately.

Keep the /data volume. It holds the generated APP_KEY, and without it the encrypted values in your database — SMTP password, SSO client secret, 2FA secrets — cannot be decrypted, even from a good backup.

Step 2 · production

Compose, with MySQL

services:
  cortendesk:
    image: ghcr.io/marcpope/cortendesk
    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>"
    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

The entrypoint waits for the database, runs migrations, creates the first administrator and rebuilds every cache on boot. Upgrading is a new image tag and a restart.


Step 3

Put TLS in front

The container serves plain HTTP on 8080 and expects a TLS-terminating proxy. With Caddy that is three lines:

console.example.com {
    reverse_proxy localhost:8080
}

Set APP_URL to the public HTTPS address and SESSION_SECURE_COOKIE=true. Traefik and nginx examples, plus the trusted-proxy settings that keep client IPs accurate, are in the reverse proxy guide.


Step 4

Point your clients at it

In a normal RustDesk client: Settings → Network. Set ID Server, Relay Server, Key, and set API Server to your console URL. The console's Settings screen shows all four ready to copy.

Devices appear within one heartbeat, about fifteen seconds.

For unattended rollout, issue an API token and use the client's --assign flag to place a machine in the right folder, with the right owner and policy, on first run.

Prefer to install it without Docker?

It is a standard Laravel deployment — PHP 8.4, MySQL or MariaDB, nginx and php-fpm.