Deployment prerequisites
What does each platform need before deploying: servers and Nomad, DNS and TLS, object storage, AI keys?
Everything here must exist before the first ansible-playbook run. Consul, Vault, Nomad,
Caddy, Docker and (on self-hosted targets) Postgres are installed by the playbooks; you bring
machines, names and a few third-party accounts.
Per target
GCP (terraform/gcp) | Vultr (terraform/vultr) | Your own servers | Multipass (scripts/up.sh) | |
|---|---|---|---|---|
| Control plane | 3 × e2-small | 3 × vc2-2c-4gb | 3 nodes | 3 × 1 vCPU, 1 GB, 6 GB disk |
| Workers | 3 × e2-standard-2 (8 GB); e2-standard-4 (16 GB) removes the headroom question | 3 × vc2-4c-8gb | 3 nodes, 8 GB or more | 3 × 3 vCPU, 9 GB, 40 GB disk |
| Worker data disk | 50 GB persistent disk each, mounted at /opt/nomad/volumes | 50 GB high_perf block storage each | a disk per worker | VM disk |
| Postgres | Cloud SQL, PostgreSQL 18, db-perf-optimized-N-4, regional HA, PITR, private IP | a database host, vc2-4c-8gb with 100 GB block storage | a database host, or a managed instance (postgres_host) | 1 VM, 1 vCPU, 2 GB |
| OS | Ubuntu 24.04 LTS | Ubuntu 24.04 LTS | Ubuntu 24.04 LTS | Ubuntu 24.04 |
| Vault unseal | Cloud KMS key from terraform/bootstrap | Shamir shares in pki/<env>/vault-init.json, or Cloud KMS via a service account key (new clusters only) | Shamir, unless you set vault_seal_gcpckms | Shamir |
| Firewall | VPC rules: 80/443 public; SSH and the control-plane APIs over IAP only (iap_members) | firewall groups: SSH from ssh_allowed_cidrs, 80/443 on workers | yours; manage_firewall: true closes the host behind it | none |
| DNS | Cloud DNS records created | tofu output -raw dns_records, added at your provider | yours | scripts/host-cluster-access.sh |
| Account | GCP project, Google account for IAP | VULTR_API_KEY in the environment | SSH key access as a sudo user | Multipass on the host |
| Inventory | written to inventory/prod.yml | written to inventory/vultr.yml (cluster_env) | hand-written from inventory/prod.yml.example | written by up.sh |
The inventory is the whole porting job: a new target fills in hosts, node_ip, the server and
client groups, nomad_volumes per worker and cluster_env_expected, and opts into
vault_seal_gcpckms, manage_firewall or starrocks_external_host if it has them.
For every cluster target
| Item | Detail |
|---|---|
| Workstation tools | scripts/prereqs.sh on Ubuntu or Debian: OpenTofu, Ansible (pipx, with psycopg2-binary), the Consul, Vault and Nomad CLIs, Node, jq, Multipass |
| Terraform state | A private Backblaze B2 bucket vantage-tfstate with versioning and a key scoped to it; B2_KEY_ID and B2_APPLICATION_KEY exported for tofu init |
| SSH key pair | ed25519; public half in terraform.tfvars, private half where Ansible reads it |
| Domain | A zone you control. Six names under it, each <label>.<domain>: analytics, auth, uploads, storage, monitoring, logs; three more for preview: analytics.preview, auth.preview, uploads.preview. There is no apex form |
| DNS resolving | Every name resolves to the workers before the first deploy |
| TLS | ingress_tls: acme with ingress_email: Caddy gets Let's Encrypt certificates for each name, so 80 and 443 must reach the workers |
| Image pull | pki/<env>/ghcr-pull.token: a GitHub personal access token with read:packages, owned by GHCR_USER in scripts/deploy.vars, before site.yml runs |
| Release and first admin | scripts/deploy.vars from deploy.vars.example: VERSION_VANTAGE, PLATFORM_ADMIN_EMAIL, PLATFORM_ADMIN_FIRST_NAME, PLATFORM_ADMIN_LAST_NAME; deploy.sh refuses to run without the admin |
| Identity | Nothing to bring: Vantage deploys its own Zitadel at auth.<domain>. Pointing it at your own identity provider is not supported. The issuer is fixed into every token, so pick the domain once |
After the deploy, in Vault
These are seeded as placeholders, so the deploy succeeds without them; each is a
vault kv patch and a job restart (Deploy and upgrade).
| Item | Vault path | Without it |
|---|---|---|
SMTP relay: host, port, user, password, from | secret/zitadel/smtp | Invitations and signup mail do not send |
B2 bucket for backups (scripts/b2-buckets.sh creates vantage-backups) | secret/vantage/b2-backups | Nightly backups stay on the worker and the run fails |
| Anthropic API key (optional) | secret/vantage/anthropic | Every AI feature and the MCP server are hidden |
| Resend key and sender (optional) | secret/vantage/resend | The weekly digest stays in the app |
| Dodo Payments key, brand, webhook secret (optional) | secret/vantage/dodo | Billing answers 503 billing_not_configured |
Object storage needs nothing: rustfs-bootstrap creates vantage-parquet-staging and its
service account on every deploy.
Local compose
| Item | Detail |
|---|---|
| Docker | 20.10 or newer (for host-gateway), with Compose v2 |
| Source | the vantage repository checked out next to vantage-deployment, or VANTAGE_DIR pointing at it |
| Hostname | vantage.localhost resolving to 127.0.0.1 (systemd-resolved, glibc and macOS do; elsewhere add it to /etc/hosts) |
| Ports free | 8186, 3103, 1026, 8026, 5462, 9031, 8031, 8041, 4001, 8201, 6380, 7234, 9021, 4446, 4445, or overridden in compose/env/vantage.env |
Limits to plan around
| Limit | Effect |
|---|---|
| External StarRocks | Queries work; ingest does not, because the dataplane stream-loads straight to the BE and does not follow a managed frontend's redirect |
| External OIDC | Not supported |
| Apex domain | Not supported; every name needs a label |
| Platform n8n | Not deployed; scheduled database syncs need an n8n you run |