Deployment Troubleshooting
This guide helps resolve common issues when deploying or running TurboPanel in production. For security-related issues, see Security. For setup verification, see Control plane and Daemon setup.
Introduction
Production issues typically fall into TLS/Caddy misconfiguration, instance socket permissions, daemon WebSocket connectivity, Postgres socket access, or Ansible playbook failures. Self-hosted browser entrypoint is HTTPS on port 8443 (Caddy). On co-located dev hosts only, Caddy also serves a plaintext mirror at http://<host>:8880 when TURBOPANEL_DEV_HTTP_CONTROL_PLANE=1.
Port conflicts (8443 / 8880)
Problem: Caddy cannot bind :8443 or (dev) :8880.
Solutions:
- Change
CADDY_PORT/CADDY_HTTP_PORTin instance/Caddy env and update firewall rules - Check for existing listeners:
ss -tlnp | grep -E '8443|8880' - Stop conflicting services or remap the published port in your reverse proxy
UI or API not loading
Problem: Browser shows errors at https://<host>:8443 or (co-located dev) http://<host>:8880.
Solutions:
- Check instance:
systemctl status turbopanel-instance - Check Caddy:
systemctl status turbopanel-caddy - Test health over HTTPS:
curl -k https://localhost:8443/api/health - Co-located dev only: test plaintext mirror:
curl http://localhost:8880/api/health(requiresTURBOPANEL_DEV_HTTP_CONTROL_PLANE=1; 403 when the flag is off) - Logs:
journalctl -u turbopanel-instance -u turbopanel-caddy -f - Static UI: ensure
ui/distexists ifTURBOPANEL_UI_MODE=static - Dev UI: ensure
turbopanel-uiis running whenTURBOPANEL_UI_MODE=dev
Daemon shows offline but service is running
Problem: UI badge is Offline (or Update fails) while systemctl status turbopaneld is active and the Cell panel shows recent activity.
Cause: The server list uses a Postgres presence projection that can drift from live WebSocket state after a failed upgrade or reconnect.
Solution: Re-run the release installer — see Refresh a stuck daemon.
Daemon not connecting
Problem: Remote server daemon fails to register.
Solutions:
- Verify instance URL:
TURBOPANEL_INSTANCE_URL=https://<host>:8443(production) or, on co-located dev only,http://<host>:8880whenTURBOPANEL_DEV_HTTP_CONTROL_PLANE=1(no CA required) - Test from node over HTTPS:
curl -k https://<host>:8443/api/daemon/v1/version - Co-located dev only: test plaintext:
curl http://<host>:8880/api/daemon/v1/version - Confirm platform CA (HTTPS path): compare with
GET /api/daemon/v1/instance/ca - Firewall: allow 8443 from remote daemons to instance (8880 is dev-only and not for remote production nodes)
- Logs:
journalctl -u turbopaneld -f
Postgres connection failures (self-hosted)
Problem: Instance cannot connect to Postgres.
Solutions:
- Check Postgres container:
docker ps | grep postgres - Verify Unix socket under
/var/run/turbopanel/postgres/ - Confirm
TURBOPANEL_DATABASE_URLinturbopanel-instance.service— a full Postgres URL, typically injected by theinstance-launchrole - Socket directory permissions:
/run/turbopanelshould be2770 tp:tp
Install wizard / PAM failures (Deno)
Problem: Install bootstrap rejects host credentials.
Solutions:
- Install
pamtesteron the host - Confirm
tpctrluser sudoers allowspamtester login * authenticate - Use a host account in
sudo/wheel/admin, orroot
UI Update stuck on "update already in progress"
Problem: After clicking Update, Ansible runs but the daemon keeps the same
PID for a long time. A second update attempt returns update already in progress.
Cause: The UI update path uses run.sh --no-start (does not stop the daemon
during reconcile). The daemon then tries to restart itself with plain
systemctl enable --now as the tp user — that fails silently
(non-root cannot restart systemd units; enable --now also skips restart when
already active). An in-memory lock then blocks further updates.
Check logs on the server:
sudo tail -30 /var/log/turbopanel/daemon.err.log
sudo grep -i 'systemctl\|sudo\|update' /var/log/turbopanel/daemon.log | tail -20Immediate recovery on the server:
sudo systemctl restart turbopaneldOr reconcile by re-running run.sh — see Refresh a stuck daemon.
Ansible / upgrade failures
Problem: Upgrade System or daemon orchestration fails.
Solutions:
- Check daemon logs — on managed hosts Ansible runs as the
tpuser; on co-located dev it runs as the current dev user - Dirty git checkouts block upgrade; commit or stash changes in
$HOMEsource repos (~/daemon,~/instance,~/ui,~/website)
General debugging
| Task | Command |
|---|---|
| Instance health (HTTPS) | curl -k https://localhost:8443/api/health |
| Instance health (dev HTTP) | curl http://localhost:8880/api/health — co-located dev only; requires TURBOPANEL_DEV_HTTP_CONTROL_PLANE=1 |
| Client status (HTTPS) | curl -k https://localhost:8443/api/client/v1/status |
| Client status (dev HTTP) | curl http://localhost:8880/api/client/v1/status — co-located dev only |
| Service status | systemctl status turbopanel-instance turbopanel-caddy turbopaneld |
| Follow logs | journalctl -u turbopanel-instance -u turbopanel-caddy -u turbopaneld -f |
| Daemon WS path (HTTPS) | wss://<host>:8443/ws/daemon/v1 |
| Daemon WS path (dev HTTP) | ws://<host>:8880/ws/daemon/v1 — co-located dev only |
Related documentation
- Security — TLS and authentication
- Control plane — Service layout
- Daemon setup — Node installer
- Dev console troubleshooting — Local dev issues
Last updated on