Domain Topology
Infrastructure is modeled in three layers:
- Core infra. One shared
core-infracluster per enterprise or major region. Hosts Forgejo, Harbor, chart publishing, and shared bootstrap services. Cloudflare R2 backs OpenTofu state and any near-term shared storage. - Shared observability. Today a
vbt-observabilitystack runs locally inside each cell (Prometheus, Grafana, Loki, Fluent Bit). A central-agent mode is on the chart's roadmap but not yet implemented. - Domain cells. Each production domain gets its own cluster/cell per environment. A domain is a product platform, sub-company, internal-ops area, regulated workload boundary, or customer-facing deployment.
Live-tree shape
tofu/live/domains/<domain>/<env>/
env.hcl
global/
dns/terragrunt.hcl
storage/terragrunt.hcl
cells/<cell>/
network/terragrunt.hcl
secrets/terragrunt.hcl
cluster/terragrunt.hcl
database/server/terragrunt.hcl
database/<instance>/terragrunt.hcl
Cell naming: use primary for the first cell. Use region-qualified names like primary-use2, dr-usw3, iot-use2, or analytics-use2 when a domain needs multiple cells.
The remote-state key includes the leaf's working-directory path, so never copy an existing leaf into a new path without a state move - Terragrunt will treat the resources as new and unmanaged.
The shape above is the full Azure-backed platform shape. Minimal Linode/K3s domains can omit leaves they do not use, such as secrets, database, global/dns, and global/storage. A specialized cell may also keep env.hcl beside its cell leaves when that cell is the whole stack.
Generic Make targets
All of these take DOMAIN=<slug> ENV=<env> CELL=<cell> (defaults: vibrant-platform, dev, primary):
- infra/plan, infra/apply - base stack (network → cluster, plus secrets/database leaves when present).
- infra/dns/plan, infra/dns/apply - DNS records; auto-skips until the env-specific ingress IP env var is set.
- kubeconfig/domain - fetch AKS credentials for Azure-backed domain cells; K3s domains should use their stack-specific kubeconfig helper.
- deploy/core, deploy/observability - Helm install into the cell.
Domain onboarding
- Pick a stable domain slug (e.g.
internal-ops,customer-portal). - Project prefix defaults to
vbt-<domain>(override withDOMAIN_PROJECT=). - Start with one primary cell per environment. Do not co-host multiple production domains on a single app cluster.
- Allocate non-overlapping VNet/pod/service/PE/IPv6 ranges in
tofu/live/domains/cidr-registry.yaml. - Choose database placement: dedicated managed server, shared managed server with logical databases, per-cell server, or Crunchy in-cluster. Document RPO/RTO, backup retention, maintenance, and failover before adding regional cells.
- DNS lives in Tofu. Charts only get their own hosts; they should never need to know other domains or cells.
- Add a domain values file when defaults aren't enough:
charts/vbt-core/envs/<domain>-<env>-<cell>.yaml. Same pattern forscripts/.env.secrets.<domain>-<env>[-<cell>].
Scaling order
- Scale inside the primary cell: HPA/KEDA, requests/limits, PDBs, larger VMs, cluster autoscaling.
- Add specialized node pools (system, app, CI/build, observability, GPU, IO-heavy).
- Add another same-domain cell in the same region for blast-radius isolation.
- Add regional cells for latency, data residency, or DR.
- Default to active/passive. Active/active requires an explicit data strategy (regional partitioning, globally replicated with conflict rules, read replicas + write primary, or app-level sharding).
Security and compliance posture
Treat every domain/cell as a future compliance boundary. Baseline: default-deny NetworkPolicies, Pod Security Standards, Key Vault purge protection, TLS at the edge, explicit admin lists, documented backup/restore. Regulated/HIPAA domains opt into stricter controls via config: private API, workload identity, per-workload Key Vault access, AKS/Key Vault/Postgres diagnostic logs, immutable audit export, Velero, image signing, admission policy, WAF/rate limiting, tested DR.