panel v0.9.1 — open-source game server manager
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+313
@@ -0,0 +1,313 @@
|
||||
# Installing panel
|
||||
|
||||
Four supported paths, in order of least effort:
|
||||
|
||||
1. [Linux one-liner](#linux-one-liner) — `install.sh` (recommended)
|
||||
2. [Windows one-liner](#windows-one-liner) — `install.ps1`
|
||||
3. [docker-compose](#docker-compose) — controller + agent + Postgres as containers
|
||||
4. [From source](#from-source) — full manual control
|
||||
|
||||
Then: [Postgres provisioning](#postgres-provisioning),
|
||||
[flags reference](#controller-flags), [systemd](#systemd),
|
||||
[reverse proxy + TLS](#reverse-proxy--real-tls), [firewall](#firewall-basics).
|
||||
|
||||
---
|
||||
|
||||
## Linux one-liner
|
||||
|
||||
Debian, Ubuntu, Fedora, CentOS/RHEL, Raspbian. Run as root:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/dbledeez/panel/main/install.sh | sudo bash
|
||||
```
|
||||
|
||||
What it does (idempotent — safe to re-run; existing containers, units,
|
||||
CA, DB and passwords are kept, binaries and modules refreshed):
|
||||
|
||||
1. Installs Docker via `get.docker.com` if missing.
|
||||
2. Starts a managed `panel-postgres` container (unless you point it at
|
||||
an existing DB with `PANEL_DB_URL`).
|
||||
3. Builds (or downloads, with `PANEL_PREBUILT_URL`) `controller`,
|
||||
`agent`, and `panelctl` into `/opt/panel/bin/`.
|
||||
4. Boots the controller once to bootstrap the internal CA and the admin
|
||||
account, then pairs the local agent over mTLS.
|
||||
5. Installs and enables `panel-controller.service` and
|
||||
`panel-agent.service`.
|
||||
|
||||
Tunables (env vars, all optional — from `install.sh`'s header):
|
||||
|
||||
| Env var | Meaning | Default |
|
||||
|---|---|---|
|
||||
| `PANEL_REPO_URL` | git/https base of the panel source | github repo |
|
||||
| `PANEL_PREBUILT_URL` | tarball with prebuilt `bin/{controller,agent,panelctl}` + `modules/` (skips the Go build) | — |
|
||||
| `PANEL_VERSION` | version string baked into the binaries | `git describe \|\| dev` |
|
||||
| `PANEL_DIR` | install prefix | `/opt/panel` |
|
||||
| `PANEL_DATA_DIR` | data directory | `$PANEL_DIR/data` |
|
||||
| `PANEL_HTTP_PORT` | dashboard port | `8080` |
|
||||
| `PANEL_GRPC_PORT` | agent gRPC port | `8443` |
|
||||
| `PANEL_ADMIN_PASSWORD` | stable first-boot admin password | generated by the controller |
|
||||
| `PANEL_DB_URL` | use an EXISTING Postgres instead of the managed container | — |
|
||||
| `PANEL_DB_PASSWORD` | password for the managed `panel-postgres` container | generated |
|
||||
| `PANEL_SKIP_AGENT=1` | controller only (add agents on other boxes later — see [PAIRING.md](PAIRING.md)) | `0` |
|
||||
|
||||
Example — controller-only box on a custom port with a pinned admin password:
|
||||
|
||||
```bash
|
||||
PANEL_SKIP_AGENT=1 PANEL_HTTP_PORT=9090 PANEL_ADMIN_PASSWORD='s3cret' \
|
||||
bash install.sh
|
||||
```
|
||||
|
||||
## Windows one-liner
|
||||
|
||||
PowerShell 5+, run **as Administrator**:
|
||||
|
||||
```powershell
|
||||
irm https://git.pdxtechs.com/dbledeez/panel/raw/branch/main/install.ps1 | iex
|
||||
```
|
||||
|
||||
**Docker Desktop** is required (it runs Postgres + your game servers). If it
|
||||
isn't installed, the installer offers to install it for you via `winget`.
|
||||
Because Docker Desktop needs WSL2/virtualization and usually a **reboot**, the
|
||||
Windows install is effectively two steps the first time:
|
||||
|
||||
1. Run the command above. If Docker is missing it installs Docker Desktop and
|
||||
then asks you to reboot + start Docker Desktop (wait for the whale icon to
|
||||
settle).
|
||||
2. Re-run the exact same command — it picks up from there and finishes.
|
||||
|
||||
To skip the prompt: `-InstallDocker` (or `PANEL_INSTALL_DOCKER=1`) auto-installs
|
||||
Docker without asking; `-SkipDockerInstall` keeps the old fail-if-missing
|
||||
behavior. On a box that already has Docker Desktop running, it's a genuine
|
||||
one-liner.
|
||||
|
||||
> **Windows note:** the controller runs great on Windows. Running game
|
||||
> *servers* on Windows uses Docker Desktop, where the agent automatically
|
||||
> switches modules from host networking to published bridge ports so they're
|
||||
> reachable — but the smoothest, fully-proven game-hosting path is a Linux
|
||||
> agent. See [NETWORKING.md](NETWORKING.md).
|
||||
|
||||
Parameters (mirror the Linux env vars): `-RepoUrl`, `-PrebuiltUrl`,
|
||||
`-Version`, `-InstallDir` (default `C:\Panel`), `-HttpPort` (8080),
|
||||
`-GrpcPort` (8443), `-AdminEmail`, `-AdminPassword`, `-DbUrl`, `-DbPassword`,
|
||||
`-DbContainer`, `-DbPort`, `-DbVolume`, `-SkipAgent`, `-InstallDocker`,
|
||||
`-SkipDockerInstall`. The same `PANEL_*` env vars are honored as defaults.
|
||||
|
||||
When run in a real console it prompts you to choose the admin email + password
|
||||
up front (no log-reading); when piped (`irm | iex`) it uses env/param values or
|
||||
generates a password.
|
||||
|
||||
The controller and agent are registered as **logon-time Scheduled
|
||||
Tasks**. For a hardened always-on Windows service, wrap the same command
|
||||
lines with [nssm](https://nssm.cc):
|
||||
`nssm install panel-controller C:\Panel\bin\controller.exe ...`.
|
||||
|
||||
## docker-compose
|
||||
|
||||
A production single-box stack (Postgres + controller + agent) lives at
|
||||
`deploy/docker-compose.yml`. From the repo root:
|
||||
|
||||
```bash
|
||||
cp deploy/.env.example deploy/.env # then edit deploy/.env (PANEL_DB_PASSWORD is required)
|
||||
docker compose -f deploy/docker-compose.yml --env-file deploy/.env up -d --build
|
||||
```
|
||||
|
||||
First run: watch `docker compose logs controller` for the
|
||||
`ADMIN BOOTSTRAP` banner (generated admin password), then mint a pair
|
||||
token in the UI and pair the agent — see [PAIRING.md](PAIRING.md).
|
||||
|
||||
> **Security note (from the compose file itself):** the agent container
|
||||
> mounts the **host** Docker socket to manage game-server containers.
|
||||
> Docker-socket access is root-equivalent on the host. Run this stack
|
||||
> only on a host you fully trust and keep the dashboard behind auth/TLS.
|
||||
|
||||
## From source
|
||||
|
||||
Prereqs: Go 1.26+, Docker, [`buf`](https://buf.build/docs/installation/)
|
||||
on PATH for proto stubs.
|
||||
|
||||
```bash
|
||||
buf generate # proto stubs (once per clone / .proto change)
|
||||
go build -o bin/controller ./controller/cmd/controller
|
||||
go build -o bin/agent ./agent/cmd/agent
|
||||
go build -o bin/panelctl ./controller/cmd/panelctl
|
||||
|
||||
# 1) Postgres (dev container) — or see "Postgres provisioning" below
|
||||
docker compose -f docker-compose.dev.yml up -d
|
||||
|
||||
# 2) Controller — first run prints the ADMIN BOOTSTRAP banner
|
||||
./bin/controller
|
||||
|
||||
# 3) Agent (same box, dev): plaintext is fine locally
|
||||
./bin/agent --insecure --modules-dir=./modules \
|
||||
--data-root=./data/instances --meta-dir=./data/instance-meta \
|
||||
--backup-dir=./data/backups
|
||||
```
|
||||
|
||||
For production, pair the agent instead of `--insecure`
|
||||
([PAIRING.md](PAIRING.md)) and run both under systemd (below).
|
||||
|
||||
There is also a root `Makefile` (`make build`, `make test`, `make vet`,
|
||||
`make install-local`, ...).
|
||||
|
||||
---
|
||||
|
||||
## Postgres provisioning
|
||||
|
||||
The controller needs Postgres (17 is what the managed container and
|
||||
compose stack use; the schema is plain SQL with embedded migrations, run
|
||||
automatically at controller start).
|
||||
|
||||
Bring your own instance:
|
||||
|
||||
```sql
|
||||
CREATE ROLE panel LOGIN PASSWORD 'choose-a-password';
|
||||
CREATE DATABASE panel OWNER panel;
|
||||
```
|
||||
|
||||
Point the controller at it via either:
|
||||
|
||||
- `--db-url 'postgres://panel:...@dbhost:5432/panel?sslmode=disable'`
|
||||
- `PANEL_DB_URL` env var (honored when `--db-url` is left at its default)
|
||||
|
||||
The default (dev) DSN is
|
||||
`postgres://panel:panel_dev@127.0.0.1:5432/panel?sslmode=disable`.
|
||||
|
||||
Or let `install.sh` run the `panel-postgres` container for you.
|
||||
|
||||
---
|
||||
|
||||
## Controller flags
|
||||
|
||||
From `controller/cmd/controller/main.go`:
|
||||
|
||||
| Flag | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `-grpc` | `:8443` | gRPC listen address (both Agent + Panel services) |
|
||||
| `-http` | `:8080` | HTTP dashboard + REST API listen address |
|
||||
| `-ca-dir` | `./data/ca` | directory holding internal CA + server cert (auto-generated on first run) |
|
||||
| `-tls` | `auto` | `auto` \| `on` \| `off` — `auto` enables mTLS if CA + server cert present |
|
||||
| `-tls-hostnames` | — | comma-separated SAN DNS names to add to the server cert (e.g. `panel.example.com`) |
|
||||
| `-log-level` | `info` | `debug`, `info`, `warn`, `error` |
|
||||
| `-db-url` | dev DSN | Postgres connection string (env `PANEL_DB_URL` also honored) |
|
||||
| `-public-url` | — | public base URL of the dashboard (needed for Steam OpenID sign-in — see [ADMIN.md](ADMIN.md)) |
|
||||
| `-initial-admin-steam-id` | — | 17-digit SteamID64 linked to the first admin, enabling Sign in with Steam ([ADMIN.md](ADMIN.md)) |
|
||||
|
||||
Env vars: `PANEL_DB_URL`, `PANEL_ADMIN_PASSWORD` (first-boot admin
|
||||
password preseed).
|
||||
|
||||
## Agent flags
|
||||
|
||||
From `agent/cmd/agent/main.go`:
|
||||
|
||||
| Flag | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `--controller` | `localhost:8443` | controller gRPC address `host:port` |
|
||||
| `--agent-id` | mTLS cert CN, or hostname when `--insecure` | agent identifier |
|
||||
| `--modules-dir` | `./modules` | path to modules directory |
|
||||
| `--data-root` | `./data/instances` | root directory for instance data |
|
||||
| `--meta-dir` | `./data/instance-meta` | per-instance sidecar metadata (survives agent restart) |
|
||||
| `--backup-dir` | `./data/backups` | where instance backup archives are written |
|
||||
| `--cert-dir` | `./data/certs` | holds `agent.crt` + `agent.key` + `ca.crt` after pairing |
|
||||
| `--heartbeat` | `10s` | heartbeat interval |
|
||||
| `--insecure` | `false` | force plaintext (no TLS) — dev/local only |
|
||||
| `--pair-token` | — | pair mode: fetch CA + signed cert from `--pair-url` and exit ([PAIRING.md](PAIRING.md)) |
|
||||
| `--pair-url` | `http://localhost:8080` | controller HTTP URL for the pair endpoints |
|
||||
| `--log-level` | `info` | `debug`, `info`, `warn`, `error` |
|
||||
|
||||
## systemd
|
||||
|
||||
Reference units live in `deploy/systemd/` (`panel-controller.service`,
|
||||
`panel-agent.service`) — `install.sh` installs them for you. Highlights:
|
||||
|
||||
- Both run as the `panel` user from `WorkingDirectory=/opt/panel`; the
|
||||
agent additionally gets `SupplementaryGroups=docker`.
|
||||
- `/opt/panel/panel.env` (optional `EnvironmentFile`) overrides
|
||||
`PANEL_HTTP_PORT`, `PANEL_GRPC_PORT`, `PANEL_DB_URL`,
|
||||
`PANEL_ADMIN_PASSWORD`, ...
|
||||
- The controller unit is hardened (`ProtectSystem=strict`,
|
||||
`ReadWritePaths=/opt/panel`); the agent's hardening is intentionally
|
||||
lighter because it drives the Docker socket.
|
||||
|
||||
```bash
|
||||
sudo cp deploy/systemd/*.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now panel-controller panel-agent
|
||||
journalctl -u panel-controller -f # ADMIN BOOTSTRAP banner lives here
|
||||
```
|
||||
|
||||
## Reverse proxy + real TLS
|
||||
|
||||
The dashboard speaks plain HTTP on `:8080`; put a reverse proxy with a
|
||||
real certificate in front of it for anything internet-facing. **Only
|
||||
proxy the HTTP port** — agents talk to gRPC `:8443` directly (mTLS,
|
||||
LAN/VPN is fine; see [PAIRING.md](PAIRING.md)).
|
||||
|
||||
The dashboard uses SSE (`/api/events`) — disable proxy buffering on
|
||||
that path or the live console will lag.
|
||||
|
||||
### Caddy
|
||||
|
||||
```
|
||||
panel.example.com {
|
||||
reverse_proxy 127.0.0.1:8080 {
|
||||
flush_interval -1 # SSE: stream immediately
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
That's the whole file — Caddy handles Let's Encrypt automatically.
|
||||
|
||||
### nginx
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name panel.example.com;
|
||||
ssl_certificate /etc/letsencrypt/live/panel.example.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/panel.example.com/privkey.pem;
|
||||
|
||||
client_max_body_size 512m; # scenario/mod zip uploads
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /api/events { # SSE — no buffering
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 1h;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the proxy terminates TLS with a public hostname and agents pair
|
||||
through it, add that hostname to the controller cert SANs:
|
||||
`-tls-hostnames panel.example.com`.
|
||||
|
||||
## Firewall basics
|
||||
|
||||
On the **controller** box:
|
||||
|
||||
| Port | Proto | Who needs it | Expose to internet? |
|
||||
|---|---|---|---|
|
||||
| 8080 (HTTP) | tcp | browsers, `panelctl admin`, pairing agents | only via the reverse proxy above |
|
||||
| 8443 (gRPC) | tcp | agents + `panelctl` | no — LAN/VPN preferred; mTLS protects it if you must |
|
||||
| 5432 (Postgres) | tcp | controller only | never |
|
||||
|
||||
On each **agent** box: no inbound panel ports at all — the agent dials
|
||||
out to the controller. You only open the **game ports** of the instances
|
||||
it hosts; see [NETWORKING.md](NETWORKING.md) for the per-game table.
|
||||
|
||||
Example (ufw, controller on a LAN):
|
||||
|
||||
```bash
|
||||
sudo ufw allow from 192.168.1.0/24 to any port 8080 proto tcp
|
||||
sudo ufw allow from 192.168.1.0/24 to any port 8443 proto tcp
|
||||
```
|
||||
Reference in New Issue
Block a user