-
Notifications
You must be signed in to change notification settings - Fork 4
Install on a VM
A standard Laravel deployment: nginx + php-fpm + MySQL. There is no installer and no frontend build step — the assets are precompiled and committed.
Examples below assume Ubuntu 22.04/24.04 and /opt/cortendesk. Adjust paths and
package names for your distribution.
-
PHP 8.3 or newer (8.4 recommended) with
pdo_mysql,mbstring,intl,bcmath,xml,curl,zip,gd,opcache - Composer
- MySQL 8 or MariaDB 10.6+ (SQLite works for evaluation only)
- nginx (or any Laravel-capable web server)
- A running open-source RustDesk server (
hbbs/hbbr) reachable from your devices - For the in-browser client: TLS, and a proxy that bridges
wss://to the hbbs/hbbr WebSocket ports 21118 and 21119
sudo apt update
sudo apt install -y nginx mysql-server git unzip \
php8.4-fpm php8.4-mysql php8.4-mbstring php8.4-intl php8.4-bcmath \
php8.4-xml php8.4-curl php8.4-zip php8.4-gdIf your distribution ships an older PHP, add a PPA (Ubuntu: ondrej/php) or use
the Docker image instead.
The RustDesk server is a prerequisite, not part of this install. If you do not have one, or yours is only half-working, see RustDesk server in Docker — hbbs and hbbr from scratch, every port, and where the key comes from.
CREATE DATABASE cortendesk CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'cortendesk'@'localhost' IDENTIFIED BY 'use-a-real-password';
GRANT ALL PRIVILEGES ON cortendesk.* TO 'cortendesk'@'localhost';
FLUSH PRIVILEGES;sudo mkdir -p /opt/cortendesk
sudo chown "$USER" /opt/cortendesk
git clone https://github.com/marcpope/cortendesk.git /opt/cortendesk
cd /opt/cortendesk
composer install --no-dev --optimize-autoloadermain always equals the latest release. To pin a version:
git checkout v0.9.0cp .env.example .env
php artisan key:generateEdit .env:
APP_NAME=CortenDesk
APP_ENV=production
APP_DEBUG=false
APP_URL=https://console.example.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=cortendesk
DB_USERNAME=cortendesk
DB_PASSWORD=use-a-real-password
# Your RustDesk server
CORTENDESK_ID_SERVER=hbbs.example.com:21116
CORTENDESK_RELAY_SERVER=hbbs.example.com:21117
CORTENDESK_PUBLIC_KEY=<contents of id_ed25519.pub>
# Native web client — wss endpoints your proxy exposes
CORTENDESK_NATIVE_WEBCLIENT=true
CORTENDESK_WS_ID_URL=wss://console.example.com/ws/id
CORTENDESK_WS_RELAY_URL=wss://console.example.com/ws/relay
# Required when served over HTTPS
SESSION_SECURE_COOKIE=trueAPP_KEY encrypts the SMTP password, the OIDC client secret and 2FA secrets.
Back it up with the database — one without the other is not a usable restore.
php artisan migrate --seed
php artisan config:cache
php artisan route:cache
php artisan view:cache--seed creates admin / changeme. Change it at first sign-in.
The web server user needs to write two directories and nothing else:
sudo chown -R www-data:www-data /opt/cortendesk/storage /opt/cortendesk/bootstrap/cache
sudo find /opt/cortendesk/storage -type d -exec chmod 775 {} \;If you run artisan by hand afterwards, run it as www-data, or it will leave root-owned cache files that php-fpm cannot write:
sudo -u www-data php artisan config:cacheserver {
listen 443 ssl http2;
server_name console.example.com;
ssl_certificate /etc/letsencrypt/live/console.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/console.example.com/privkey.pem;
root /opt/cortendesk/public;
index index.php;
client_max_body_size 64m;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
location ~ /\.(?!well-known).* {
deny all;
}
# Web client: bridge wss to the RustDesk server. Browsers refuse ws:// from
# an HTTPS page, which is the whole reason these two blocks exist.
location = /ws/id {
proxy_pass http://127.0.0.1:21118/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
}
location = /ws/relay {
proxy_pass http://127.0.0.1:21119/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
}
}
server {
listen 80;
server_name console.example.com;
return 301 https://$host$request_uri;
}Point proxy_pass at wherever hbbs/hbbr actually listen — 127.0.0.1 only if
they run on this same VM.
sudo nginx -t && sudo systemctl reload nginxSkip the two /ws/ blocks if you are not using the in-browser client. The
console itself works without them.
Log retention, pruning and other maintenance run through Laravel's scheduler. Without this they never run.
sudo crontab -u www-data -e* * * * * cd /opt/cortendesk && php artisan schedule:run >> /dev/null 2>&1
On each RustDesk client, Settings → Network: ID Server, Relay Server, Key,
and API Server = https://console.example.com. Devices appear within about
fifteen seconds. The console's Settings screen shows copy-paste values for all
four fields.
For unattended deployment, create an API token under Settings → Security and use
the client's --assign flag.
If something else terminates TLS and forwards to this VM over plain HTTP, the
app honours X-Forwarded-* headers already. Set APP_URL to the public HTTPS
URL and SESSION_SECURE_COOKIE=true. If your proxy sits outside the private
address ranges, set TRUSTED_PROXIES in .env to its address — see
Reverse proxy and TLS.
cd /opt/cortendesk
sudo -u www-data git pull
sudo -u www-data composer install --no-dev --optimize-autoloader
sudo -u www-data php artisan migrate --force
sudo -u www-data php artisan optimize:clear
sudo -u www-data php artisan config:cache
sudo -u www-data php artisan route:cache
sudo -u www-data php artisan view:cacheBack up the database first. See How to Upgrade.
mysqldump -u cortendesk -p cortendesk > cortendesk-$(date +%F).sql
cp /opt/cortendesk/.env /somewhere/safe/ # holds APP_KEYA database backup without APP_KEY cannot decrypt the SMTP password, the OIDC
client secret, or anyone's 2FA secret. Keep both.
php artisan cortendesk:import-lejianwen /path/to/rustdeskapi.db --dry-run
php artisan cortendesk:import-lejianwen /path/to/rustdeskapi.dbUsers keep their original password hashes, devices are deduplicated, and address books, share rules and audit history come across. Address-book entry passwords encrypted by the Go API cannot be decrypted and must be re-saved by users.
500 on every page. Almost always permissions. Check
storage/logs/laravel.log, then confirm storage and bootstrap/cache are
owned by www-data.
Changes to .env do nothing. The config is cached. Run
sudo -u www-data php artisan config:cache after every edit.
No devices appear. The clients' API Server is not pointing here. Confirm the value in the client, and that this host is reachable from the device.
Web client will not connect. The /ws/ blocks are missing, point at the
wrong host, or CORTENDESK_WS_*_URL is not the wss:// URL the browser sees.
Sign-in loops back to the login page. SESSION_SECURE_COOKIE=true without
HTTPS, or an APP_URL that does not match the address in the browser.
Locked out by SSO or 2FA. Set CORTENDESK_OIDC_DISABLED=true in .env, or
run sudo -u www-data php artisan cortendesk:2fa-reset <username>.