Skip to content

3.3 Deployment topologies and sizing

Part 3 · Architecture & operationsArchitect⏱ ~30 min✓ Knowledge check

SpatiumDDI runs on one VM or across a multi-node cluster. Adding DNS and DHCP servers never needs a reinstall, and on the OS appliance neither does growing the control plane into a cluster. The decisions that are hard to undo come first: which shape, which install method, and how big. This module walks through the seven reference topologies from the docs, the three ways to install, the sizing numbers the project has measured, and the ports each piece needs.

  • Match an environment to one of the seven reference topologies
  • Choose between Docker Compose, the Helm chart and the OS appliance
  • Size a control plane and agent nodes from the measured numbers, and know where those numbers stop
  • List the ports each component needs and apply the fleet firewall basics
  • api and frontend are stateless: run as many as you like behind a load balancer.
  • worker scales out too, sharded by queue. The bundles queue renders the DNS agents’ config.
  • beat is always exactly one.
  • PostgreSQL goes HA with CloudNativePG, on Kubernetes and on the appliance. Compose HA isn’t supported in 1.0 (see topology 4).
  • Redis goes HA with Sentinel.
  • DNS and DHCP agents run one per server, and you add servers as you need them.

Because agents serve from their cache, a control-plane outage doesn’t take DNS or DHCP down with it. That’s what makes the separated topologies below worth the extra machines.

vm-spatium.lab.local4 vCPU · 8 GB · 50 GB SSD — supports ~5,000 IPs / 50 subnets / 50 zones docker-compose.yml — single host apiFastAPIworkerCelerybeatSchedulerfrontendnginx + Vitemigratealembic postgresprimary write sourcerediscache · brokerbind9DNS — :53 udp/tcpkea-dhcpDHCP — :67 udp Backup local_volume → /var/lib/spatiumddi/backups (named volume)or remote — S3 / SCP / Azure / SMB / FTP / GCS / WebDAV / NFS / HTTPS PUT
Topology 1: everything on one host · Adapted from the SpatiumDDI documentation (Apache-2.0)

Everything runs in one Docker Compose stack: api, worker, beat, frontend, migrate, PostgreSQL, Redis, BIND9 and Kea. It’s the simplest setup and a single backup unit. But every service shares the host (a DHCP storm can slow the API down) and there’s no HA. Use it for labs, homelabs and trials; your Academy lab is a variant of it.

2. Control plane plus separate DNS and DHCP servers

Section titled “2. Control plane plus separate DNS and DHCP servers”
operators / API nginx / LB vm-spatium-cp.corp.localcontrol plane — 4 vCPU · 8 GB · web :80 behind the LB (443 at the LB) · API :8000 on localhost only api worker beat frontend postgres redis vm-dns.corp.localdocker-compose.agent-dns-bind9.yml bind9:53 udp/tcp dns-agentlong-poll JWT long-poll /api/v1/dns/agents/config (ETag) vm-dhcp.corp.localdocker-compose.agent-dhcp.yml kea:67 udp dhcp-agentlong-poll JWT long-poll /api/v1/dhcp/agents/config (ETag)
Topology 2: agents at the network edge · Adapted from the SpatiumDDI documentation (Apache-2.0)

This is the shape most production deployments reach for first. The control plane gets its own VM, and DNS and DHCP agents run on dedicated machines at the network edge, long-polling the API. A control-plane outage doesn’t stop DNS or DHCP, the edge servers get their own firewall rules, and you size each box for its own workload.

Plain Docker agents use the standalone compose files (docker-compose.agent-dns-bind9.yml, docker-compose.agent-dhcp.yml) with the bootstrap key. They verify the control plane’s TLS certificate, so with a private CA or a self-signed certificate, mount the CA and set TLS_CA_PATH (#1220). OS appliances installed as an Additional node pair with an 8-digit code instead.

nginx LB API + DBsingle VM (Topology 2) DNS Server Group vm-dns-01is_primarybind9 :53 vm-dns-02own copybind9 :53 vm-dns-03own copybind9 :53 DHCP HA pair (Kea) vm-dhcp-eastprimarykea :67 vm-dhcp-weststandbykea :67 HA Configured via the UI DNS Server Group — every member renders its own copy; each record change is an op per server. The zone'sper-server serial status shows who has converged. DNSSEC signing needs a group with one server. DHCP Server Group with HA mode hot-standby (or load-balancing). HA covers DHCPv4 only.Set each Kea server's own ha_peer_url (appliances fill it in). Peer IPs are re-resolved if they drift. Agents long-poll the control plane; if the API is unreachable, both sides serve from cache and Kea HAcontinues operating against its peer regardless of control-plane state.
Topology 3: redundant DNS and a Kea HA pair · Adapted from the SpatiumDDI documentation (Apache-2.0)

Two or more DNS servers sit in one group, and records render to all of them. A Kea HA pair (hot-standby or load-balancing, DHCPv4 only) sits in one DHCP group. Each host is its own VM. Use this when DNS or DHCP downtime isn’t acceptable; module 3.4 covers the mechanics.

4. HA control plane with Patroni and Redis Sentinel

Section titled “4. HA control plane with Patroni and Redis Sentinel”
operators / API consumers nginx / haproxyTLS termination · sticky LB for SSE vm-api-01api · frontendworker vm-api-02api · frontendworker vm-beat (single)celery beat —not horizontally scalable Redis Sentinel — 3 nodes vm-redis-01mastervm-redis-02replica + sentinelvm-redis-03replica + sentinel PostgreSQL cluster — 3 nodes (Patroni or similar, built by you) vm-pg-01primaryvm-pg-02replica + etcdvm-pg-03replica + etcd HAProxy in front: a read/write port (the primary) and a read-only port (replicas) restores go to the read/write port Hand-built: SpatiumDDI 1.0 ships no supported Compose HA (the repo's Patroni overlay doesn't work).
Topology 4: a hand-built HA control plane · Adapted from the SpatiumDDI documentation (Apache-2.0)

Two or more API hosts sit behind a load balancer, with a 3-node Patroni cluster for PostgreSQL, a 3-node Redis Sentinel set, and a single beat. If you installed the OS appliance, use topology 7 instead.

Cloud region (eu-west-1 / east-us / etc.) Spatium control planeHA api + HA PostgreSQL (a managed service or CloudNativePG) + HA Redis api ×N worker postgres HA redis HA backup S3 Cloud LB — TLSspatium.example.com long-poll over HTTPS via VPN / WireGuard / Tailscale Branch / on-prem corp network vm-dns-eastbind9 + dns-agentserves LAN clients :53 vm-dhcp-east (Kea HA)kea + dhcp-agentserves LAN clients :67 vm-dns-westbind9 + dns-agentserves LAN clients :53 LAN clients query the on-prem agents directly — cloud control plane is for management only. If the tunnel drops, agents serve from cache and replay their spooled logs and leases when it returns.
Topology 5: control plane in the cloud, agents on-prem · Adapted from the SpatiumDDI documentation (Apache-2.0)

The control plane runs in your cloud account, and DNS and DHCP agents stay on-prem: DNS should answer close to the clients, and DHCP has to be on the same broadcast domain as its clients. Agents long-poll over HTTPS and can lose the connection for hours while they serve from cache; what they collect meanwhile (query logs, lease events, metrics) is spooled to disk and replayed when the link returns.

Expect 80 to 150 ms per UI or API round trip across regions, terminate TLS at the cloud load balancer (ACME or your own certificate), and let audit, SMTP and webhook traffic out of the cloud side.

Kubernetes 1.31+ cluster — 3+ nodes (separate node pools optional) api Deployment — HPA 2 to 10 (CPU)Service (cluster-internal)stateless · the autoscaler sets the replica count worker ×2 (fixed replicas)Celery + bundles queueNET_RAW for nmap device profiling beat Pod ×1Singletonnot horizontally scalable migrate Jobone per Helm revisionnot a hook · pods wait in wait-for-migrate CloudNativePG — 3 instances, asyncprimaryRW :5432replicareplica Redis StatefulSet — sentinel sidecarsmasterreplicareplica dns-bind9 StatefulSetbind9 + agent sidecarClusterIP (default) dhcp-kea StatefulSet (group-centric HA)kea + agent sidecarhostNetwork / relay Ingress + cert-managerspatium.example.com PVC: spatium_backups (RWX)required for local_volume backup target —see k8s/README
Topology 6: the umbrella Helm chart · Adapted from the SpatiumDDI documentation (Apache-2.0)

The umbrella Helm chart runs every component: an api Deployment that its autoscaler runs at 2 to 10 replicas on CPU, fixed-replica workers and frontend (2 each by default), one beat pod, a migrate Job per Helm revision, CloudNativePG, Redis Sentinel and optional DNS and DHCP agent StatefulSets. Pick it when Kubernetes is already your platform.

operators / DNS · DHCP agents MetalLB control-plane VIPone floating IP · L2 · re-homes on node loss appliance-1 · etcd seedk3s server + etcdapi · frontend · workerCNPG primaryredis · sentinel (master) appliance-2 · memberk3s server + etcdapi · frontend · workerCNPG replicaredis · sentinel (replica) appliance-3 · memberk3s server + etcdapi · frontend · workerCNPG replicaredis · sentinel (replica) embedded-etcd quorum (3 / 5 / 7) · CNPG primary + async replicas · Redis Sentinel scaled by promoting Additional nodes in /appliance → Fleet · DNS and DHCP run only on nodes given those roles
Topology 7: appliances promoted into a cluster · Adapted from the SpatiumDDI documentation (Apache-2.0)

Install the first appliance as the First node, install two more as Additional nodes, and promote them from the Fleet UI into a 3-node cluster (later 5 or 7). You get embedded etcd, CloudNativePG, Redis Sentinel and a MetalLB VIP. The docs call it the answer to “I want HA without becoming a Postgres + Kubernetes operator”.

Two things to know before you rely on it. CloudNativePG replicates asynchronously, so a failover can lose the last few commits. And never grow a cluster by installing a second First node: that creates a separate SpatiumDDI, not a member.

Moving between topologies is additive. From 1 to 2 to 3, new machines join the same control plane with the agent bootstrap key and the database doesn’t move. On the appliance you grow from 1 to 3 to 5 nodes by promoting, with no reinstall.

Interactive · Which reference topology?

Answer a few questions about the install. The result is a starting point: adding DNS and DHCP servers never needs a reinstall, and on the OS appliance neither does growing the control plane into a cluster. Moving a Docker Compose install to an HA control plane does mean a new install, so weigh that one up front.

The README’s use case: dev and small single-host production.

  • Needs Docker Engine 25+ and Compose v2.20+, and at least 2 vCPU and 4 GiB of RAM (4 vCPU and 8 GiB recommended).
  • Before first start, change POSTGRES_PASSWORD and set SECRET_KEY in .env (openssl rand -hex 32). The api refuses to start on the placeholder or on a key under 32 characters (#1222).
  • Pin the release in .env (SPATIUMDDI_VERSION=1.0.0-beta.3). Betas are GitHub pre-releases, so the latest images don’t move to them.
  • The API port is published on 127.0.0.1 only (API_BIND); browsers and remote agents use the web port.
  • TLS is yours to add: a reverse proxy on the host, Caddy, or TLS inside the frontend’s nginx. Behind your own proxy, set the External URL (Settings → Application → Branding & URL) and FRONTEND_TRUSTED_PROXY_CIDRS (see the reverse-proxy guide in Go deeper).
  • Compose HA isn’t supported in 1.0. For HA, use the appliance (topology 7) or Kubernetes with CloudNativePG.
  • The appliance fleet firewall doesn’t apply; firewall the host with your own tooling.

The sizing guide gives a measured Minimum and a Recommended size per shape, from appliances, Compose and Kubernetes installs measured in October 2026:

Shape Minimum Recommended
Docker Compose, one host 2 vCPU, 4 GiB 4 vCPU, 8 GiB
Kubernetes, the umbrella chart one node: 4 vCPU, 6 GiB 4 vCPU, 8 GiB per node
Appliance, one node (control plane, with or without DNS and DHCP) 4 vCPU, 6 GiB, 40 GiB disk 4 vCPU, 8 GiB, 40 GiB SSD
Appliance, three-node control plane first node 4 vCPU and 8 GiB, two more 2 vCPU and 6 GiB, 40 GiB disks every node 4 vCPU, 8 GiB, 48 GiB SSD
Appliance role (a DNS and/or DHCP agent box) 3 GiB (its pods don’t schedule below that) 2 vCPU, 4 GiB, 32 GiB SSD; not yet measured

Topology 1 on Compose has its own figure in the topologies guide: 4 vCPU, 8 GB and a 50 GB SSD comfortably handle about 5,000 IPs, 50 subnets and 50 zones. What the measurements show:

  • Memory is mostly the stack, not the load. One appliance node used 3.3 GiB at rest and 4.1 GiB in the busiest suite, and the same on 8 GiB. The 6 GiB Minimum is the measured floor, not a comfortable size, and the installer warns below 8 GiB.
  • Disk is set by upgrades. Each upgrade imports a full set of images onto /var, and the appliance keeps the previous release’s for rollback. On 40 GiB disks a slot upgrade followed by a rolling upgrade reached the default-on appliance_disk_usage warning (80%; critical at 87%), hence 48 GiB for a cluster. Give every control-plane node 64 GiB or more for about a million devices.
  • Large DNS groups need memory. Plan for more from 250,000 records up. Each DNS server’s container (agent and daemon under one limit, 2Gi by default) fits a million records only while its resolver cache stays small, and wants 3Gi if it also recurses.
  • SSD is strongly preferred for the etcd and PostgreSQL write path.

An older single-node test, from September 2026, loaded one DNS group of 250,000 A records (plus 250,000 PTRs) and 35,000 DHCP devices (20,000 active). 8 GiB with 4 vCPU passed with headroom, 6 GiB with 3 vCPU was the smallest size that passed every time, 4 GiB wedged, and 2 vCPU was CPU-bound.

In that test 500,000 records in one group didn’t fit and 1,000,000 didn’t converge, because the api built each server’s bundle itself. The worker has rendered DNS bundles since 2026.10.02-1 (#1111), so treat those two figures as dated, and keep splitting very large namespaces across groups until you’ve tested your own.

One more finding matters for HA designs: control-plane HA is not serving HA. In the 3-node drill, DNS and DHCP roles sat on the seed node only, so while it was down the survivors answered about 5% of queries. Assign DNS and DHCP roles to every node you expect to keep serving.

The guide is open about its gaps: Compose and Kubernetes were measured only at rest and under light load, nobody has yet measured a DNS-only or DHCP-only appliance or one node under sustained realistic load, and no run lasted longer than 3.3 days.

The guide also shows how to measure your own install: Appliance → Cluster → Overview on the appliance, docker stats on Compose, kubectl top on Kubernetes. Size from your largest used-memory sample times 1.5, plus room to grow.

Traffic Port Protocol Notes
Web UI (Compose) 8077 (HTTP_PORT) HTTP Operators to the frontend; the API and its docs are under /api on the same port
Web UI with TLS, and the appliance 443 (80 also open on the appliance) HTTPS Operators to the frontend
API direct 8000 (API_PORT) HTTP Compose publishes it on 127.0.0.1 only (API_BIND)
PostgreSQL / Redis 5432 / 6379 TCP Internal only, never exposed
Agents to the control plane your control-plane URL HTTPS Agents only call out, and they verify its certificate
DNS service 53 UDP, TCP Standalone Compose agent files map 1053 on the host by default
DHCP service 67 (68 on the client side); 547 for DHCPv6 UDP Standalone Compose agent files map 6767 by default
Kea HA peer to peer 8000 TCP Between a group’s Kea servers only; HTTPS with client certificates once the group’s credential is active
DoT / DoH / DoQ 853 / 443 / 853 TCP / TCP / UDP Per group, off by default; use 8443 for DoH on the appliance
Windows servers 5986 (HTTPS) or 5985, and 53 TCP; UDP and TCP Control plane to Windows: WinRM, RFC 2136 and AXFR
FortiGate 443 HTTPS Control plane to FortiOS REST
Appliance cluster 6443, 2379/2380, 10250, 7946, 19443 TCP (7946 also UDP) Control-plane peers only; 19443 is the frr-k8s webhook MetalLB uses for BGP
SSH to an appliance 22 TCP Open by default; only Enforce source restriction can scope it

The PowerDNS and Technitium APIs (127.0.0.1:8081, 127.0.0.1:5380) listen on loopback inside their containers and are never exposed.

The appliance ships a declarative firewall you author on the control plane and the supervisor applies to each node. Compose and generic Kubernetes installs firewall their hosts with their own tools.

  • Off by default, behind two gates: the appliance.firewall feature module, and the firewall_enabled enforcement switch.
  • Layered policy: a fleet baseline, then per-role overlays (DNS engines, DHCP, control plane), then per-appliance overrides. SSH on 22, ICMP and loopback form a floor no rule can remove, and no rule may drop port 22. Only the separate SSH source restriction (Enforce source restriction, ssh_lockdown, off by default) can scope SSH (#1009).
  • Per role: DNS-only and DHCP-only nodes open only their service ports (53; 67/68 and 547 for DHCP) plus the floor, and never etcd or kubelet. Control-plane members open etcd, kubelet, MetalLB’s 7946 and the 19443 webhook to their cluster peers only, never LAN-wide. Kea HA members open the HA port to their partners only.
  • Enforcement is gated: it refuses to turn on until every node reports hardened. Stage and review changes in Preview changes first.
  • Web UI access: a card on the Firewall tab limits ports 80/443 (and the VIP) to source CIDRs you list. It rejects a list that doesn’t cover your own IP unless you override it, and a Web UI or SSH change that would leave no remote way in at all needs an explicit console-only acknowledgement (#1013).
Knowledge check · 6 questions

Deployment topologies and sizing

1. A one-campus college can live with the UI being down for an hour, but not with DNS or DHCP outages. Which topology is the right starting point?

2. Why does the sizing guide recommend a 48 GiB SSD for each node of a three-node appliance control plane, when a single node is fine on 40 GiB?

3. You reinstall the Helm chart under a new release name, pointing at the existing database, and stored credentials no longer decrypt. Why?

4. In a 3-node appliance cluster, the DNS and DHCP roles are assigned to the seed node only. The seed goes down. What happens?

5. Which of these must stay internal and never be published to the network?

6. Which statements about the appliance fleet firewall are true?

Select all that apply.

Finished 3.3 Topologies & sizing?
Mark it complete to track your progress (stored only in this browser).