Panel and Daemon deployment
This page starts with a single Ubuntu test host. Nginx terminates Panel HTTPS, Panel listens on loopback, a separate TLS Gateway accepts Daemon control connections, and the Daemon runs with rootless Docker. Production replicas require PostgreSQL, Redis and a shared Artifact Store; do not use the test stack's SQLite and local-storage choices for multiple Panel replicas.
Single-host test deployment
Prepare an Ubuntu 26.04 x86-64 host with at least 2 CPU, 4 GiB memory and 20 GiB free disk. Allow inbound TCP 22, 80, 443 and 9443. Install matching Panel and Daemon release artifacts; do not mix protocol versions or deploy a development working tree.
Create separate gamex-panel and gamex system accounts. Run Panel behind Nginx on a loopback listener, install rootless Docker for gamex, and keep the system Docker socket disabled. Complete Panel Setup over HTTPS, enable the Gateway, create a one-time Daemon enrollment link, then run sudo gamex on the host and paste the link only into its hidden prompt.
Keep administrator passwords, TOTP seeds, recovery codes, enrollment links and SSH keys outside the documentation and source checkout. Store operational secrets in the deployment's secret manager or a root-owned file with mode 0600.
Verify the deployment independently:
sudo gamex doctor
sudo systemctl is-active nginx.service gamex-panel.service gamex-daemon@gamex.service
curl --fail https://<public-ip>/api/health/ready
sudo curl --unix-socket /run/gamex-daemon/admin.sock http://localhost/status
openssl s_client -connect <public-ip>:9443 -servername <public-ip> -verify_return_error -briefRequire every service to be active, rootless Docker diagnostics to pass, Gateway certificate verification to succeed and the Daemon status to report controlConnected as true.
Enroll additional Daemons
In Daemons, create a short-lived one-time enrollment link for the intended host. Install gamex and gamex-daemon on that host and run:
sudo gamex configure
sudo gamex doctor
sudo systemctl status gamex-daemon@gamex.service
sudo curl --unix-socket /run/gamex-daemon/admin.sock http://localhost/statusPaste the complete enrollment link only into the hidden prompt. Do not place it in a command argument or shell history. Repeat with a different link and a unique Daemon name for every host. Before placing a Workspace, confirm the expected architecture, CPU, memory, disk, rootless Docker socket and online state in Daemons.
Gateway and trust rotation
Generate private Gateway material for a production deployment outside the Web Setup flow:
npm --prefix gamex-panel/backend run gateway:bootstrap -- \
--control-url wss://panel.example.com:9443/api/v1/daemon/connectKeep the generated key at mode 0600. Give every live Gateway replica a unique DAEMON_GATEWAY_INSTANCE_ID. During CA or leaf rotation, first deploy a bundle containing both old and new roots, canary the new leaf, reconnect one Daemon, roll the remaining replicas, and remove the old root only after every Daemon is healthy. Restore the old leaf while the overlap bundle remains active if the canary fails.
Production acceptance
- The load balancer uses
GET /api/health/ready, not the liveness endpoint. - Panel replicas share one deployment identity, PostgreSQL, Redis and Artifact Store but have unique Gateway instance IDs.
- The Panel port is private; only HTTPS and the mTLS Gateway are public.
- System Docker stays disabled on Daemon hosts; the Daemon uses the dedicated rootless socket.
- At least one Panel-only Chat succeeds before any Daemon is enrolled, then a harmless Workspace read succeeds on each enrolled Daemon.
For pre-release deployment and incident procedures, continue with Deployment, rollback and recovery.