Add image-based deployment for control panels
CI / Lint & Type Check (push) Failing after 3m5s
CI / Daemon Build & Test (push) Failing after 13s
CI / Docker Build (push) Has been skipped
CI / Publish images (push) Has been skipped

docker-compose.panel.yml deploys every service from a published image.
A control panel writes only a compose file and an .env into its project
directory, so the build: stanzas of docker-compose.yml cannot resolve
their context there.

CI pushes api, migrate, web and daemon images to the Gitea container
registry on v* tags. The migrate stage ships as its own image because
the panel compose runs it as a one-shot service before the API starts.

The web port is named HOST_PORT: panels reverse-proxy "the" port of an
installation and need to know which one that is when a stack publishes
more than one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
hibna
2026-08-02 20:39:58 +03:00
parent 11924416a9
commit 7ca55bc94d
3 changed files with 269 additions and 0 deletions
+76
View File
@@ -533,6 +533,82 @@ sudo ufw enable
---
## 4. Control Panel Deployment (pre-built images)
Sections 2 and 3 build from a checkout on the server. A control panel does not
have one: it writes a compose file and an `.env` into its own project directory
and runs `docker compose up`. Anything with a `build:` stanza fails there —
the build context simply is not on disk.
`docker-compose.panel.yml` exists for that case. Every service references a
published image, so the stack installs on a server that has never seen this
repository. It was written against [WebPanel](https://gits.hibna.com.tr/hibna/Source-WebPanel)
but nothing in it is panel-specific.
### 4.1 Publish the images
`.github/workflows/ci.yml` pushes four images to this Gitea instance's own
container registry on every `v*` tag:
| Image | Contents |
|---|---|
| `gamepanel-api` | Fastify API |
| `gamepanel-migrate` | The API Dockerfile's `migrate` stage, run once before the API starts |
| `gamepanel-web` | SPA + nginx, built with `VITE_API_URL=/api` |
| `gamepanel-daemon` | Rust daemon |
Add a `REGISTRY_TOKEN` repository secret with package write scope, then:
```bash
git tag v0.1.0 && git push origin v0.1.0
```
### 4.2 Prepare the host
```bash
sudo mkdir -p /etc/gamepanel /var/lib/gamepanel/servers /var/lib/gamepanel/backups
sudo cp daemon-config.yml /etc/gamepanel/daemon-config.yml
sudo sed -i 's/CHANGE_ME_GENERATE_A_SECURE_TOKEN/'"$(openssl rand -hex 32)"'/' /etc/gamepanel/daemon-config.yml
```
Note the token you generated — the panel needs the same value when you register
the node. If the panel has a file manager, both steps can be done from it.
### 4.3 Install
Paste `docker-compose.panel.yml` into the panel's custom-compose screen and set:
| Variable | Example | Notes |
|---|---|---|
| `REGISTRY` | `gits.hibna.com.tr/hibna` | Namespace holding the four images |
| `TAG` | `v0.1.0` | The tag you pushed |
| `HOST_PORT` | `8096` | **Not 80** if the panel's own web server owns it |
| `DB_PASSWORD`, `REDIS_PASSWORD` | `openssl rand -hex 24` | |
| `JWT_SECRET`, `JWT_REFRESH_SECRET` | `openssl rand -hex 64` | |
| `CORS_ORIGIN` | `https://panel.example.com` | Must match the address the browser uses |
The published port is called `HOST_PORT` because panels commonly reverse-proxy
"the" port of an installation and need to know which one that is when a stack
publishes more than one.
If the registry is private, the host needs `docker login` once — panels pull
anonymously otherwise. On Gitea the package can also be made public while the
repository stays private.
### 4.4 Notes
- **The daemon holds the Docker socket.** That is root-equivalent access to
every container on the machine, the panel's own containers included. Running
the daemon on a separate node — which the multi-node architecture is built
for — keeps the game hosts and the control plane apart.
- **Nothing publishes gRPC on a single host.** The API reaches the daemon over
the compose network as `daemon:50051`. A remote node runs the `daemon`
service on its own machine and publishes `50051` there.
- **Game server ports** are opened by the daemon on the host; a panel with a
default-deny firewall needs an explicit rule for the range you hand out.
---
## Post-Installation
### First Login