# Panel — PRODUCTION single-box compose (postgres + controller + agent). # (Dev Postgres-only compose lives at ../docker-compose.dev.yml.) # # Usage, from the repo root: # cp deploy/.env.example deploy/.env # then edit deploy/.env # 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 create a pair token in the UI # (or via POST /api/admin/pair-tokens) — the agent pairs itself on start # only if you exec the pairing step; see README / install.sh for the flow. services: postgres: image: postgres:17-alpine restart: unless-stopped environment: POSTGRES_USER: panel POSTGRES_PASSWORD: ${PANEL_DB_PASSWORD:?set PANEL_DB_PASSWORD in deploy/.env} POSTGRES_DB: panel volumes: - panel_pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U panel -d panel"] interval: 5s timeout: 3s retries: 10 controller: build: context: .. dockerfile: deploy/Dockerfile.controller args: VERSION: ${PANEL_VERSION:-dev} restart: unless-stopped depends_on: postgres: condition: service_healthy environment: PANEL_DB_URL: postgres://panel:${PANEL_DB_PASSWORD}@postgres:5432/panel?sslmode=disable # Optional: stable first-boot admin password instead of a generated one. PANEL_ADMIN_PASSWORD: ${PANEL_ADMIN_PASSWORD:-} ports: - "${PANEL_HTTP_PORT:-8080}:8080" # dashboard + REST API - "${PANEL_GRPC_PORT:-8443}:8443" # agent gRPC (mTLS) volumes: - panel_controller_data:/opt/panel/data agent: build: context: .. dockerfile: deploy/Dockerfile.agent args: VERSION: ${PANEL_VERSION:-dev} restart: unless-stopped depends_on: - controller # ┌────────────────────────────── SECURITY ──────────────────────────────┐ # │ The agent mounts the HOST Docker socket to create and manage game- │ # │ server containers. Docker-socket access is ROOT-EQUIVALENT on the │ # │ host: anyone who can reach this container's filesystem or process │ # │ can own the machine. Run this stack only on a host you fully trust, │ # │ keep the panel dashboard behind auth/TLS, and never re-export the │ # │ socket from this container. │ # └───────────────────────────────────────────────────────────────────────┘ volumes: - /var/run/docker.sock:/var/run/docker.sock # IMPORTANT: the agent passes instance paths to the HOST docker daemon # as bind mounts, so the host path and in-container path MUST match. # Keep both sides of this mapping identical. - ${PANEL_AGENT_DATA:-/opt/panel/data}:${PANEL_AGENT_DATA:-/opt/panel/data} environment: PANEL_GRPC_PORT: "8443" command: - "--controller" - "controller:8443" - "--modules-dir" - "./modules" - "--data-root" - "${PANEL_AGENT_DATA:-/opt/panel/data}/instances" - "--meta-dir" - "${PANEL_AGENT_DATA:-/opt/panel/data}/instance-meta" - "--backup-dir" - "${PANEL_AGENT_DATA:-/opt/panel/data}/backups" - "--cert-dir" - "${PANEL_AGENT_DATA:-/opt/panel/data}/certs" volumes: panel_pgdata: panel_controller_data: