Production quick start: multi-region fleet
flowchart TB
ExternalDNS["External management DNS"] -. "publishes names" .-> CONTROL
subgraph Management["Management region"]
CONTROL["Control plane"] --> PG[("PostgreSQL")]
EDGEAPI["edge-control"] --> CONTROL
CONTROL -->|"revisions"| DNSAPIS["Restricted DNS APIs"]
TELEMETRY["Telemetry + Grafana"]
end
subgraph Regions["Regional POPs"]
Agents["Edge agents"] -->|"outbound mTLS"| EDGEAPI
DNSAPIS --> PDNS["Private PowerDNS"]
DNSDIST["DNSdist"] --> PDNS
GATEWAYS["Gateway + bounded cells"] --> Origins["Validated origins"]
end
Resolvers["Resolvers"] --> DNSDIST
Clients["HTTP clients"] --> GATEWAYS
DNSDIST -. "telemetry" .-> TELEMETRY
GATEWAYS -. "telemetry" .-> TELEMETRY
Keep management DNS outside CDNFoundry
Publish control.<operator-zone>, edge-control.<operator-zone>, telemetry.<operator-zone>, and every dns-api-N.<operator-zone> through an independent external DNS provider. Do not put the operator zone in CDNFoundry PowerDNS. CDNFoundry PowerDNS contains derived platform/customer runtime state and must not be required to find the services that repair or manage it.
This example models one control node, four authoritative DNS nodes, ten edge nodes, and three monitoring-role nodes. “Multi-region” describes its failure-domain design; it is not a special runtime mode or a fixed scale limit.
Read and complete the starter fleet quick start first. The same security, PKI, transfer, migration, enrollment, last-valid-state, backup, and acceptance rules apply.
Clone an immutable source revision
git clone https://github.com/vaheed/CDNFoundry.git cdnfoundry
cd cdnfoundry
git checkout v1.0.0
git rev-parse --verify HEAD
sudo ./scripts/install-production-prerequisites.shConfigure the topology without editing scripts
install -m 0600 deploy/production/examples/multi-region-fleet.json ./fleet.jsonEdit fleet.json and replace the operator/platform domains, exact release, ACME contact, every documentation address, hostname, region, and location. Add private monitor_ipv4/log_ipv4 addresses when monitoring traffic must avoid public paths. For remote PostgreSQL, add typed control-node extra_env values for DB_HOST, DB_PORT, and DB_SSLMODE, then replace control-db-password through set-secret --from-file; never put the password in JSON.
Validate without writing:
python3 -m json.tool fleet.json >/dev/null
./scripts/cdnfoundry-fleet --config fleet.json --non-interactive --dry-run setupCreate state and all node bundles:
sudo ./scripts/cdnfoundry-fleet \
--config fleet.json \
--state-dir /var/lib/cdnfoundry-fleet \
--output-dir /var/lib/cdnfoundry-fleet/bundles \
--non-interactive \
setupRoll out in dependency order
- Start
control-1, including its MMDB updater, database dependencies, migrations, control services, and health checks. - Start the selected dedicated monitoring host and qualify Prometheus, Grafana, ClickHouse, Loki, and log ingestion.
- Start all four DNS nodes. Register, test, and enable their DNS clusters; apply NS/glue platform identity only afterward and wait for every cluster to acknowledge it. Qualify UDP/TCP answers before delegation.
- Create each edge in the control panel, paste its two-line UUID/token block into the matching prepared host, and run
sudo docker compose --env-file .env.prod --profile edge up -done host at a time. Edge display and Fleet node names may differ. - Confirm each agent persisted its identity and sent a fresh heartbeat; no rerender or second transfer is required. Add
--profile edgeto the localstart.shCompose command for future whole-host starts if desired. - Validate regional fallback, draining, node loss, control-plane outage, restart, telemetry loss, and previous-bundle rollback.
Do not activate all hosts simultaneously. A target must be valid and serving before any source is drained. Generated .env.prod is authoritative for every role bundle; do not add deployment defaults to Compose or edit rendered manifests on a host.
See Production fleet configuration reference for JSON fields and Production fleet operator guide for lifecycle commands.