Deploy the control plane
Run the plane's api, hub and reactor with Postgres: processes, prerequisites, required settings and plane networking.
This page covers the plane: api, hub, reactor, the migration job and Postgres. The gateway has its own page, Deploy the gateway.
There is no Helm chart for the plane yet (see the roadmap). The sections below describe the processes, what each needs and the decisions you make. Translate them into Kubernetes manifests, your own chart or another orchestrator.
The compose.yml stack in the repository is for local development. It turns admin authentication off. Do not deploy it.
Processes
All four plane processes run the plane image.
| Process | Command | Listens on | Replicas |
|---|---|---|---|
| migrate | migrate upgrade head | nothing; runs to completion | one, before each rollout |
| api | api | :8000 HTTP: the API, /health, /metrics, and the console at / | one or more |
| hub | hub | :8001 WebSocket at /v1/hub/sync, plus /health | one or more |
| reactor | python -m app.reactor | nothing | optional |
The reactor runs inside the hub by default. To run it as its own process, set HANGAR_REACTOR_ENABLED=false on the hub and start the reactor separately. A lease in Postgres makes a second reactor wasteful but safe.
The hub scales without sticky sessions. A reconnect that lands on a different replica costs that cluster a full resync instead of a resume.
Prerequisites
Required configuration
Every plane setting is a HANGAR_ environment variable. The full table is in Plane configuration. These are the ones a deployment must decide.
| Variable | Set on | What |
|---|---|---|
HANGAR_DATABASE_URL | migrate, api, hub, reactor | postgresql+psycopg://user:password@host:5432/db |
HANGAR_APP_ENV | every plane process | production, which is the image default. Never local outside a laptop |
HANGAR_ADMIN_PASSWORD_HASH | api | bcrypt hash of the admin password |
HANGAR_SESSION_SIGNING_SECRET | api | random secret that signs admin sessions |
HANGAR_GATEWAY_SERVICE_TOKEN | api, gateway | random secret the gateway authenticates to the plane with |
HANGAR_HUB_PUBLIC_URL | api | the wss:// URL clusters dial the hub on. Written into each cluster’s setup values. HANGAR_PUBLIC_HOST does not stand in for it |
HANGAR_TUNNEL_GATEWAY | api | host:port of the gateway tunnel, written into each cluster’s setup values |
HANGAR_OPERATOR_IMAGE, HANGAR_AGENT_IMAGE | api | the images clusters install, pinned to a tag |
HANGAR_SETUP_CHART_REF, HANGAR_SETUP_CHART_VERSION | api | where clusters pull the cluster-setup chart from |
Set these only when they apply:
| Variable | When |
|---|---|
HANGAR_SETUP_CHART_PRIVATE=true | the chart registry needs helm registry login. The console adds the login step to every cluster’s setup instructions |
HANGAR_IMAGE_PULL_SECRET_* | your image registry is private |
HANGAR_ALLOWED_ORIGINS | the console is served from a different origin than the api |
Outside local, the plane refuses to start when a required secret is missing, so a misconfigured deployment fails at boot instead of running open.
Generate the admin password hash with the image you built:
docker run --rm -it registry.example.com/hangar/plane:<tag> python -m app.auth hash
The password must be at least 12 characters. Pass --stdin to read it from standard input instead of prompting.
Human access is a single admin role. See Security for the auth model.
Run it
Run the migration
Run migrate upgrade head with the plane image and wait for it to finish.
Start the hub and api
Start hub and api with the settings above. Start the reactor too if you run it separately.
Check health
GET /health on :8000 and on :8001 should succeed.
Sign in
Open the api’s address in a browser and sign in to the console with the admin password.
Then deploy the gateway and enrol a cluster. Rolling to a new build is covered in Upgrades.
Plane networking
| Path | Who connects | How to expose it |
|---|---|---|
api (:8000) | the console in a browser, hgr | HTTPS ingress, or keep it private and use kubectl port-forward |
api /metrics | your Prometheus | Scrape each api pod on the cluster network. /metrics needs no credential, so refuse it at the ingress that serves the api |
hub (:8001, /v1/hub/sync) | every cluster’s operator | HTTPS ingress that supports WebSocket upgrades and long-lived connections. Raise idle timeouts well above a minute |
Clusters only make outbound connections, to the hub and to the gateway tunnel. The plane never connects into a cluster. Gateway listeners are covered on the gateway page.