This page is a runbook designed to be read by an AI agent (Claude Code, Cursor, Windsurf, or similar). The agent reads this page, asks the human user the intake questions below in order, and then executes the install against their cloud account.
If you are a human reading this directly, you probably want the cloud-specific guide: AWS, GCP, or Azure. Come back here when you’d rather have an agent drive.
Quick-start: paste this into your agent
Open a fresh session in Claude Code (or Cursor, Windsurf, etc.) in an empty working directory, and paste:
Help me set up Definite. Look at https://docs.definite.app/on-prem/agent-setup.md
That’s it. The agent will fetch this page, ask the questions in the Intake section, then drive Phases 2-5 against your cloud account.
In the default path, the human should only need to provide cloud credentials and answer the intake questions. Do not ask them to pre-create DNS, paste a Definite setup token, or paste a Definite license key unless they explicitly choose customer-owned/manual mode.
For agents: every page on this site is also published as plain Markdown by appending .md to the URL. Use the .md URL when you fetch a doc because it is smaller, has no JSX components, and is easier to parse. So /on-prem/aws is at https://docs.definite.app/on-prem/aws.md, /on-prem/gcp at https://docs.definite.app/on-prem/gcp.md, and so on.
If your agent supports MCP, also add the Definite docs as a source so it can pull in the per-cloud guides on demand: claude mcp add --transport http definite-docs https://docs.definite.app/mcp.
Before you begin: accounts, licensing, and login
If you’re a human about to point an agent at this page, these are the questions that usually come first.
Do I need a Definite account first? No. You do not need an account at definite.app (the SaaS app) to run Definite on-prem. In the default flow the only things you bring are:
- cloud credentials for AWS, GCP, or Azure — you stay signed into your own cloud CLI, and
- answers to the Intake questions below.
How do I get a license? In the default Definite-brokered flow, you don’t request or paste one. During definite init, the CLI proves control of your active cloud account to Definite — a presigned STS GetCallerIdentity request on AWS, a gcloud identity token on GCP — and Definite issues a short-lived setup token, a *.onprem.definite.app hostname, and the license automatically, injecting all three into the install. Nothing to obtain ahead of time and nothing to copy into config.yaml. The deployment then activates that license against Definite’s control plane and caches a signed entitlement that it renews on its own.
If you’d rather hold the key yourself (customer-owned / manual mode), Definite issues it to you directly — see Q9. Without a license the deployment still boots, but every product API route returns 403 until it activates.
How do I log in once it’s up? Your login to your instance is the initial admin you set in config.yaml (auth.initial_admin_email + auth.initial_admin_password) — see Bootstrap the initial admin. That admin user is created inside your own deployment on first boot; it is not a definite.app account. From there you add more users locally or wire up SSO.
Who do I talk to at Definite? The brokered self-serve path above needs nothing but cloud credentials and an agent. If you’d instead like Definite to scope or operate the deployment, or you need a manual license key, email hello@definite.app.
For the agent: how to use this page
You are an AI coding agent helping a human install on-prem Definite into their own cloud account. Read this entire page before asking your first question. Follow these rules:
- Ask one question at a time. Do not batch the intake questions. Wait for the human’s answer before moving on.
- Run real commands against the human’s cloud account. Do not just print them. Use your shell tool. Confirm before any destructive operation (delete, drop, force-destroy).
- Never invent values. If the human hasn’t given you a region, project ID, hostname, password, or credential, ask. Placeholders like
<your-region> must be replaced before any command runs.
- Capture every value into a single
config.yaml as you go. Don’t ask the human to write the file; you write it, show it to them, and confirm before definite init.
- Run
definite doctor before definite init. Do not pass --skip-preflight unless doctor failed only on the Postgres reachability check from a laptop outside the VPC (private RDS / Cloud SQL / Postgres Flexible Server) and the per-cloud guide says that is expected. Never use --skip-preflight to mask Kubernetes, object-store, license, or LLM failures.
- Hand off to the per-cloud guide for the long shell sequences. Phase 2 below tells you which page to fetch.
- Stop and escalate to a human at Definite (
hello@definite.app) if: the cloud lacks a permission you need, an LLM provider rejects the model, definite doctor fails for a reason you can’t fix in three tries, the broker rejects cloud-attested setup-token acquisition, or the human asks for production support beyond install.
Agent note: Do not stop the install to ask the user to configure customer DNS unless they explicitly selected customer-owned DNS mode. In the default flow, use Definite-brokered DNS. After provisioning the load balancer, call the Definite setup broker, use the returned *.onprem.definite.app URL, wait for DNS/TLS readiness, and continue the install. Customer-owned DNS can be configured after the system is healthy.
Agent note: do not ask the user for a Definite setup token in the default flow. If DEFINITE_ONPREM_SETUP_TOKEN is not already present, the released definite CLI proves control of the user’s cloud account to Definite and obtains a short-lived setup token automatically. On AWS this uses a presigned STS GetCallerIdentity request. On GCP this uses a gcloud identity token. The raw token is not printed and expires quickly.
Phase 0: Confirm the agent’s environment
Before asking the human anything, verify your own tooling. Run, in parallel:
which kubectl helm
kubectl version --client 2>/dev/null
helm version --short 2>/dev/null
You need kubectl 1.28+ and helm 3.12+. If either is missing, install them before continuing. The cloud CLI (aws, gcloud, or az) is checked in Phase 2 once you know which cloud.
Phase 1: Intake
Ask the human these questions in order. Capture each answer into a working scratchpad you can refer back to in later phases.
Q1. Which cloud?
“Which cloud are you installing on: AWS, GCP, or Azure?”
Record as CLOUD. This drives every subsequent decision.
If CLOUD = | Per-cloud guide to fetch in Phase 2 | Cluster | Postgres | Object store | Default LLM |
|---|
| AWS | /on-prem/aws | EKS | RDS Postgres 15 | S3 | Bedrock |
| GCP | /on-prem/gcp | GKE (Autopilot) | Cloud SQL Postgres 15 | GCS (HMAC) | Vertex AI |
| Azure | /on-prem/azure | AKS | Postgres Flexible Server 15 | Blob Storage | Azure OpenAI |
Azure is currently a preliminary guide. If the human picks Azure, tell them this is not yet validated end to end, and recommend they email hello@definite.app to be a design partner before proceeding.
Q2. Environment
“Is this a demo / pilot install (you’ll throw it away in a few weeks), or a production install (real users, real data)?”
Record as ENV ∈ {demo, prod}. This changes a handful of defaults:
| Setting | Demo | Production |
|---|
| HA databases | single-AZ | multi-AZ / regional |
| Deletion protection | off | on |
| Hostname | Definite-brokered https://<slug>.onprem.definite.app | Definite-brokered https://<slug>.onprem.definite.app by default; customer-owned DNS is an advanced option after the system is healthy |
| TLS | Definite-brokered TLS | Definite-brokered TLS by default; private/internal production may need customer certs or a private CA |
| Backups | none | daily snapshots |
| Lakehouse PVC size | 50Gi | size to expected data + 2x |
Do not ask for DNS before first install unless the human explicitly chooses customer-owned DNS mode. The default first-run URL comes from Definite.
Q3. DNS mode
“Should I use the default Definite-brokered DNS for the first install? Definite will provide a URL like https://<slug>.onprem.definite.app. Choose customer-owned DNS only if you already need a private/internal hostname or custom certificate.”
Record as DNS_MODE ∈ {definite-brokered, customer-owned}. Default to definite-brokered.
If DNS_MODE = definite-brokered, do not ask for a hostname yet. After the load balancer exists, call the Definite setup broker through the definite CLI, record the returned URL as PUBLIC_URL, and set HOSTNAME to the host portion, for example <slug>.onprem.definite.app.
If DNS_MODE = customer-owned, ask:
“What customer-owned hostname should Definite be reachable at, like definite.acme.com?”
Record as HOSTNAME, then follow the per-cloud guide’s customer-owned DNS notes. Use the released CLI’s customer-owned mode when it is available; the expected flag shape is --dns-mode customer-owned --hostname <HOSTNAME>.
Private/internal production deployments may require customer-owned DNS and a customer certificate or private CA. That is a custom production path, not the default first-run path.
Q4. Authentication
“How should users sign in: local auth (email + password, simplest), or OIDC SSO (Google Workspace, Okta, Entra ID, etc.)?”
Record as AUTH ∈ {local, oidc}.
If AUTH = oidc, also ask:
“Which OIDC provider? I’ll need the issuer URL, client ID, and client secret.”
If they want Google Workspace SSO specifically, point them at /on-prem/sso for the OAuth client setup, and continue.
Q5. LLM provider
“Which LLM provider should Fi use?”
CLOUD | Default | Alternatives |
|---|
| AWS | Bedrock (IRSA, no static creds) | Anthropic direct, Azure OpenAI, Vertex |
| GCP | Vertex AI (Workload Identity, no static creds) | Anthropic direct, Bedrock, Azure OpenAI |
| Azure | Azure OpenAI | Anthropic direct, Bedrock, Vertex |
Record as LLM_PROVIDER. For Bedrock or Vertex, confirm the human has model access enabled in their account for the Claude model they want.
Q6. Region
“Which region? Pick whatever is closest to your users / source data. If you’re not sure, [recommend their cloud’s most-popular region].”
Record as REGION. Cross-check that the chosen LLM model is available in this region (Bedrock and Vertex availability varies).
Q7. Existing infrastructure to reuse
“Are you starting fresh, or do you have existing infrastructure you want me to reuse: an existing Kubernetes cluster, an existing Postgres database, or an existing object-store bucket?”
Most installs are greenfield. If the human says they want to reuse something, capture the connection details and note them for Phase 2: you’ll skip provisioning that resource but still need it in config.yaml.
Q8. Naming
“What name prefix should I use for the resources I create? Default is definite.”
Record as NAME_PREFIX. This becomes part of cluster name, bucket name, DB name, etc.
Q9. Optional customer-owned license
Do not ask for a manual license key in the default Definite-brokered flow. definite init requests a broker-issued license during setup and injects it for the install.
Only ask this question if the human explicitly selected customer-owned/manual mode:
“On-prem Definite needs a license key, issued to you by Definite. Do you have it? It looks like onprem_ followed by a long hex string.”
Record as LICENSE_KEY only for that manual path. If the human does not have a key for manual mode, stop and have them email hello@definite.app.
Phase 2: Provision cloud infrastructure
You now have enough information to provision. Fetch the per-cloud guide for CLOUD and execute Phase 1 (“Provision …”) of that guide, substituting the answers from Intake.
CLOUD | Fetch (use the .md URL) and follow |
|---|
| AWS | /on-prem/aws.md - Phase 1 (first supported black-box path, Terraform module) |
| GCP | /on-prem/gcp.md - Phase 1 (gcloud sequence) |
| Azure | /on-prem/azure.md - Phase 1 (az sequence; preliminary) |
A few rules that apply across all three:
- Confirm the cloud CLI is authenticated before running anything.
aws sts get-caller-identity / gcloud config list account / az account show. If the human isn’t logged in, ask them to authenticate; do not try to authenticate as them.
- Capture every output you’ll need for
config.yaml as you go (cluster name, DB connection string, bucket name, access keys, service-account emails). Don’t make the human re-run commands to find them.
- Get explicit confirmation before any command that costs money (cluster create, RDS create, Cloud SQL create) or that writes IAM bindings.
- Do not create customer DNS in the default flow. For
DNS_MODE = definite-brokered, continue until the load balancer exists, then use the definite CLI to call the setup broker. The backend contract is POST /onprem/v1/setup-sessions, but the agent should not ask the human to run a direct curl by default.
- Provisioning takes 20-40 minutes, mostly the managed Kubernetes cluster. Tell the human that up front. Run independent commands in parallel where possible.
When Phase 1 of the per-cloud guide is done, return here for Phase 3.
Phase 3: Assemble config.yaml
Open a new file config.yaml in the human’s working directory and fill it in from your scratchpad. The shape is the same across clouds; only object_store, lakehouse.storage.storage_class_name, and llm change.
A canonical template (replace every <...> with a real value from Intake or Phase 2):
deployment:
name: <NAME_PREFIX>
namespace: <NAME_PREFIX>
dns_mode: definite-brokered # default first-run mode
broker:
# Optional. Omit in the default path: definite init will acquire a short-lived
# setup token by proving control of the active AWS/GCP credentials.
# setupToken:
# env: DEFINITE_ONPREM_SETUP_TOKEN
# Omit the license block in the default brokered flow. definite init injects
# the broker-issued license for this install. Add license.key only for
# customer-owned/manual mode.
postgres:
url: postgres://<user>:${POSTGRES_PASSWORD}@<host>:5432/<db>
object_store:
# type: s3 | gcs | azure
# See the per-cloud guide for the exact credentials shape.
lakehouse:
prefix: lake/
storage:
size: 50Gi
storage_class_name: <gp3 | premium-rwo | managed-csi-premium>
auth:
mode: <local | oidc>
initial_admin_email: <admin@your-domain>
initial_admin_password:
env: INITIAL_ADMIN_PASSWORD
# If oidc: also set issuer, client_id, client_secret env ref.
llm:
provider: <bedrock | vertex | anthropic | azure_openai>
# Provider-specific fields per the per-cloud guide.
resources:
api: { replicas: 2, cpu: "1", memory: 2Gi }
lakehouse: { replicas: 1, cpu: "4", memory: 16Gi }
frontend: { replicas: 2, cpu: 500m, memory: 512Mi }
job_runner: { replicas: 1, cpu: 500m, memory: 1Gi }
Then show the filled-in config.yaml to the human and ask them to confirm before you proceed. Diff it against their answers. Flag anything you had to guess.
Export the secrets the file references:
export POSTGRES_PASSWORD="..."
export INITIAL_ADMIN_PASSWORD="$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 24)"
# Object-store credentials (S3 access key, GCS HMAC pair, or Azure storage key)
# OIDC_CLIENT_SECRET if auth.mode: oidc
Pull these values from Phase 2’s outputs. Never echo secrets back into the chat transcript; reference them by env var name only. In the default brokered flow there is no LICENSE_KEY env var and no DEFINITE_ONPREM_SETUP_TOKEN env var; the CLI acquires both broker authorization and license during definite init.
Do not commit config.yaml to a public repo while the secret env vars are exported in your shell. For prod installs, recommend sourcing secrets from a secret manager (AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, 1Password).
Phase 4: Install Definite
The CLI install is identical across clouds. Run, in order:
# 1. Install the CLI
curl -fsSL https://storage.googleapis.com/definite-public/definite-onprem/install.sh | sh
definite version
# 2. Bootstrap cluster-level prereqs (ingress, cert-manager, agent-sandbox CRDs)
definite bootstrap
# 3. Preflight
definite doctor --config config.yaml
# 4. Deploy. In the default mode, init discovers the ingress load balancer,
# calls the Definite setup broker, waits for brokered DNS readiness, injects
# the returned hostname/license into this install, and prints the final URL.
# If no setup token is configured, the CLI acquires one automatically from
# the active AWS/GCP credentials.
definite init --config config.yaml --dns-mode definite-brokered
If DNS_MODE = customer-owned, use definite init --config config.yaml --dns-mode customer-owned --hostname <HOSTNAME> instead. Do this only when the human explicitly selected customer-owned DNS; otherwise use Definite-brokered DNS.
If definite doctor fails, fix the failure before running definite init. The most common failures and fixes are in the Troubleshooting section of each per-cloud guide.
The one documented exception is the Postgres reachability check against a private DB endpoint from a laptop (RDS, Cloud SQL with private IP, Postgres Flexible Server). The in-cluster API pods may reach the DB fine while the laptop has no route. If doctor fails only on Postgres for this reason, follow the per-cloud guide’s private-network workaround. Do not use --skip-preflight to mask any other failure.
In the default Definite-brokered flow, config.yaml should not have a static license block. If definite init or post-install smoke tests still report UNLICENSED, brokered license acquisition or injection failed. Capture the CLI output and API logs, retry once after confirming cloud attestation still works, then escalate to Definite. For customer-owned/manual mode only, verify the provided license.key and re-run.
Bootstrap the initial admin
For local auth, include auth.initial_admin_email and auth.initial_admin_password.env in config.yaml before running definite init. The CLI passes those values into the Helm install, and the API creates the initial admin on first startup.
Print the admin password back to the human and tell them to save it in a password manager. It is not recoverable.
Do not manually patch INITIAL_ADMIN_EMAIL or INITIAL_ADMIN_PASSWORD onto the API Deployment after install unless you are repairing an older deployment. Do not restart the lakehouse unless logs show an unrelated lakehouse-specific failure.
AWS Bedrock follow-up
If CLOUD = AWS and LLM_PROVIDER = bedrock, confirm the definite ServiceAccount has the Bedrock IRSA role ARN. If it is not already present, annotate it after install:
BEDROCK_ROLE_ARN=$(terraform output -raw bedrock_irsa_role_arn)
kubectl annotate serviceaccount definite -n <NAME_PREFIX> \
eks.amazonaws.com/role-arn="$BEDROCK_ROLE_ARN" --overwrite
kubectl rollout restart deploy/definite-api -n <NAME_PREFIX>
kubectl rollout status deploy/definite-api -n <NAME_PREFIX>
Without this annotation Fi can’t call Bedrock and every Fi conversation fails. See AWS guide → Bedrock IRSA.
Phase 5: Verify
definite status --config config.yaml — all pods Running, ingress has an address.
- Open
https://<HOSTNAME> in a browser. In the default flow this is the Definite-brokered https://<slug>.onprem.definite.app URL; wait until the broker reports DNS/TLS ready before debugging the app.
- Log in with the admin email + password from Phase 4.
- Confirm core auth routes return 200 after login (
/login, /auth/me, and the main app shell).
- Run a simple query smoke, for example
SELECT 1 AS ok.
- If Fi is configured, ask Fi a question against a test connection.
If any of these fail, capture logs (definite logs api --tail 200) before debugging.
Hand-off to the human
When Phase 5 is green, summarize for the human:
- The URL they sign in at
- The admin email + a reminder where the password is saved
- The path to
config.yaml (they’ll need it for definite upgrade)
- The per-cloud guide’s Day-2 section (
/on-prem/<cloud>#day-2-operations)
- Next steps: Connect a data source, set up the MCP server, add SSO if they started with local auth
- For teardown later: the per-cloud guide’s Phase 3. On AWS in particular, the ingress-nginx NLB must be deleted before
terraform destroy or the destroy hangs on subnet DependencyViolation.
When to escalate
Stop and tell the human to email hello@definite.app if:
- The cloud account is missing a permission you can’t grant yourself, and the human can’t grant it either.
- The LLM provider rejects model access and the human’s admin doesn’t respond within an hour.
definite doctor fails three times for distinct reasons.
definite init succeeds but pods crash-loop with errors that aren’t in the per-cloud Troubleshooting table.
- The human asks for anything beyond install (capacity planning, custom registry mirrors, air-gapped install, FIPS compliance, custom sandbox network policies).
Email hello@definite.app with reproducible bugs.