Repository navigation
Troubleshooting
Grouped by symptom. Most reports come down to one of the first three.
Almost always the client's API Server setting, not CortenDesk.
- On the client: Settings → Network. API Server must be the console URL, including scheme and port — the same address you open in a browser.
- The client must be able to reach it. Test from the machine itself.
- Wait one heartbeat, about 15 seconds.
If devices still do not appear:
- Is the approval gate on? With Settings → Server → require device approval enabled, new devices land in a pending tab and are invisible until approved. This looks exactly like nothing happening.
- Was the device deleted? A device in the recycle bin has its heartbeats deliberately ignored, so it will never come back on its own. Restore it.
- Key mismatch. A public key with a stray trailing newline or space fails exactly like a wrong key. Re-paste it with no surrounding whitespace.
Symptom: the client connects fine to any ID while signed out. Sign in to use the address book and every connection fails after about 18 seconds with
Failed to secure tcp: deadline has elapsed
The devices stay online in the console the whole time, and the address book loads normally. Only connecting from the signed-in client breaks.
Cause: the ID server. Clients from 1.4.1 onwards encrypt their connection to
the ID server whenever they hold a console access token, and they begin by
waiting for the server to send a signed key. The open-source RustDesk hbbs
never sends one, so the client waits and gives up. Signed out there is no token,
no encryption, and no problem — which is what makes it look like the address
book is at fault when it is not.
Fix: run CortenDesk Server for hbbs and hbbr. It answers the exchange. Same binaries, ports, flags and data files, so switching is a change of image and a restart, and every device keeps its ID and key.
Workaround, if you cannot change the server right now: turn on Settings → Network → Use WebSocket on the machine you connect from. Clients skip the exchange on that transport. It needs ports 21118 and 21119 reachable, and it routes sessions through the relay rather than direct.
Signing out also "works", at the cost of the address book. It is worth knowing as a diagnostic — if signing out fixes it, this is what you are looking at.
Raise Settings → Server → online window (default 60 seconds). A device counts as offline once its last heartbeat is older than that, so lossy links can make devices appear to bounce.
Work through these in order.
-
Do the WebSocket URLs match the page's scheme?
CORTENDESK_WS_ID_URLandCORTENDESK_WS_RELAY_URLmust be reachable from the browser, not from the container, and must bewss://on an HTTPS console — a browser refusesws://from an HTTPS page as mixed content. Over plain HTTP, usews://. HTTPS itself is not required; see Web client and HTTPS. - Is the proxy reachable from the browser? See Reverse proxy and TLS.
-
Does your proxy forward WebSocket upgrades on
/ws/idand/ws/relay? - Check the browser console for a mixed-content or upgrade error — it names the failure directly.
- Browser support. The stream requires WebCodecs; use Chrome or Edge.
Testing WebSocket endpoints with
curlover HTTPS is misleading. curl negotiates HTTP/2, where theUpgradeheader has no meaning, so a perfectly healthy endpoint reports 502 or times out. Useopenssl s_clientand look for101 Switching Protocols.
-
2FA device lost —
php artisan cortendesk:2fa-reset <username> -
Emailed sign-in codes required but mail is broken —
php artisan cortendesk:email-verification off -
Password sign-in hidden and the identity provider is down — set
CORTENDESK_OIDC_DISABLED=truein the environment and restart. Note that a broken-but-still-configured provider does not restore the password form on its own. - Locked out by repeated failures — the sign-in limiter is per account and per address, and clears on its own within a minute.
See the command-line reference.
Make sure APP_URL uses https:// and your proxy sends X-Forwarded-Proto.
SESSION_SECURE_COOKIE=true makes the browser drop the session cookie on plain
HTTP. Remove it, or use the https:// address. Left unset, cookies are Secure
on HTTPS requests and HTTP on the LAN still signs in.
Use the test send in Settings → Email — it reports the provider's own error, which is usually conclusive. The two common causes:
- the from address is not one your relay permits, or
- credentials or encryption mode do not match what the relay expects.
Your reverse proxy is not trusted, so the app records the proxy's address instead of the real client's.
The default already trusts loopback and every private range
(127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16), which covers a proxy
on the same host or a Docker bridge. So you normally hit this in one of two
situations:
-
Your proxy reaches the app from a public address — Cloudflare, or an
external load balancer. Add its ranges to
TRUSTED_PROXIES. -
You set
TRUSTED_PROXIESyourself and left out the range your proxy actually arrives from.
Note that TRUSTED_PROXIES must be read at config-cache time, so set it as an
environment variable before the app starts rather than expecting a later change
to be picked up.
Left unfixed this is worse than a cosmetic issue: device last_online_ip is
wrong, and the per-address sign-in limiter treats every user as one address and
starts locking people out. See Reverse proxy and
TLS.
Sessions from machines that rebooted, dropped off the network or had their service stopped are closed by a scheduled sweep. If they accumulate, the scheduler is not running.
Docker runs it automatically. A manual install needs the cron entry:
* * * * * cd /path/to/cortendesk && php artisan schedule:run >> /dev/null 2>&1
The same cron entry drives log pruning, so if logs are also growing without bound, this is the cause.
Notifications stop, sessions stay open and Diagnostics shows no fresh
scheduler heartbeat, even though the cron entry runs. On releases before
1.10.1 this happens when PHP has pcntl_signal in disable_functions, which
many hosting panels set: each task ran once and then stayed locked for 24
hours. Upgrade to 1.10.1 or later, then clear the stuck locks once:
php artisan schedule:clear-cacheRetention is 0, which means keep everything, or the scheduler is not running.
See above and Audit logs.
RustDesk 1.5.0 clients with a console before 1.11.0 stored each connection,
file transfer and alarm three times, and the copies stayed on the dashboard as
active sessions. Upgrade to 1.11.0, then remove the copies with
cortendesk:dedupe-audits.
Requests queue when every PHP worker is busy, and nginx refuses connections
when it runs out of open files. The Docker image sizes both for larger fleets
since 1.11.0; see sizing in Docker
and on a VM. Look for
server reached pm.max_children setting or Too many open files in the log.
Console link: policy pull failed, keeping the last policy: ... Connection refused ...
Console link: reachable again
Right after the container starts this is harmless. hbbs asks the console for
the server access policy every few seconds and
started before the console was answering. When reachable again follows, the
link works.
If it does not follow:
-
HTTP 401: the console andhbbshave differentCORTENDESK_SERVER_SECRETvalues. -
HTTP 404: the console has noCORTENDESK_SERVER_SECRET. - Connection refused or a timeout that persists:
CORTENDESK_CONSOLE_URLon a separatehbbsdoes not reach the console.
Diagnostics → ID server link shows when hbbs last fetched the policy.
The /data volume is not writable. Recreate it rather than fixing ownership by
hand.
Open an issue with your install method, version, and what you have already ruled out from this page.