--- id: self-hosted-install title: Install Vantage (self-hosted) sidebar_label: Self-hosted install --- This installs the control plane on a host you own. Budget about fifteen minutes. ## Before you start You need: - A Linux host with **Docker** and the **Compose plugin**. - A DNS name pointing at it. You will use it for both the web UI and, with a port, for agents. - A reverse proxy terminating TLS in front of the web UI. gRPC on `:9090` is reached directly by agents. - Two ports reachable from every machine you intend to manage: the web port for people, and **9090** for agents. - Outbound access from the control plane, and from every managed machine, to `gitea.hostxtra.co.uk`, which serves the agent releases. The stack itself brings MongoDB, Redis and guacd with it. You do not need to provide a database. ## 1. Get the compose file Put `deploy/docker-compose.yml` from the repository in a working directory, for example `/opt/vantage`. ```bash mkdir -p /opt/vantage/data && cd /opt/vantage # copy docker-compose.yml here ``` The `server` service bind-mounts `./data`, which is where workflow run logs are written. Create it before first boot so it is not owned by root-in-container in a way you did not intend. ## 2. Write the environment file Create `/opt/vantage/.env`: ```bash # The host:port agents dial. NOT the web URL this port speaks gRPC. GRPC_HOST=vantage.example.com:9090 # 32 bytes as 64 hex characters. Generate with the command below. KEY_ENCRYPTION_KEY= # Optional: where workflow run logs are written inside the container. ``` Generate the encryption key: ```bash openssl rand -hex 32 ``` :::danger Keep the encryption key `KEY_ENCRYPTION_KEY` encrypts SSH private keys, vault secrets, OIDC client secrets and console credentials with AES-256-GCM. Lose it and every one of those becomes unreadable there is no recovery path. Back it up somewhere other than the server it protects, and never rotate it without a planned re-encryption. ::: :::warning `GRPC_HOST` has no default The server refuses to boot without it. There is deliberately no fallback to the web host: that would hand every agent a port that does not speak gRPC, and the failure would only surface later, on each agent, as a connection error. ::: ## 3. Start the stack ```bash docker compose up -d docker compose ps ``` Five services come up: `mongo`, `redis`, `guacd`, `server` and `web`. Check the server got through boot: ```bash docker compose logs -f server ``` Boot runs database migrations, builds indexes and seeds the default workflow step library. Index builders for auth and settings are **fatal on failure** — they enforce tenant isolation, so the server would rather not start than start without them. ## 4. Put a proxy in front Point your reverse proxy at `web` on port `3000` and terminate TLS there. The web app calls the REST API through a Next rewrite, so you do not need to expose `8080` publicly. Do **not** proxy `9090`. Agents connect to it directly over TLS. ## 5. First sign-in Open your hostname in a browser. With no users in the database, you are sent to `/setup`. Continue with [First login](./first-login.md). ## Verifying the install | Check | Expected | | ---------------------------------------------- | ------------------------------- | | `docker compose ps` | five services `running` | | `curl -s localhost:8080/auth/bootstrap-status` | JSON saying bootstrap is needed | | `nc -z your-host 9090` | open | | `docker compose logs server \| grep -i fatal` | nothing | ## Common install problems **Server exits immediately.** Almost always a missing `GRPC_HOST`. The log line names it. **Agents register but never go active.** They reached `:9090` for `Register` but cannot sustain the poll, or `GRPC_HOST` names a host they resolve differently. Check from the managed machine, not from the control plane host. **Secrets pages error.** `KEY_ENCRYPTION_KEY` is empty or not 64 hex characters. More in [Troubleshooting](../reference/troubleshooting.md). ## What this install does not include The website, the HQ portal and this documentation site are hosted by us and are not part of a self-hosted install. It deliberately runs none of them, and in particular never holds the licence signing key.