Appearance
Architecture
Nucleus has two layers, and they typically run on different infrastructure.
The control plane
A NestJS service plus its Postgres database, a Redis instance for the BullMQ provisioning queue, the Vue frontend (if you choose to run the optional portal — Self-Host doesn't ship it), and a reverse proxy with TLS.
The control plane is not Kubernetes-native. It runs fine on a single Linux VM with Docker Compose. It talks to your tenant cluster as a client — kubeconfig file mounted into the container, kubectl and helm invoked as outbound calls.
A reasonable production deployment is one Linux host (1 vCPU / 1 GB RAM is enough for a first deploy; more if Postgres gets hot) running:
- Postgres 16 (control-plane database)
- Redis 7 (BullMQ + the federation event stream)
- The control-plane container
- A reverse proxy (Caddy is what we ship; nginx works fine)
The tenant cluster
A Kubernetes cluster that runs your tenants. Every hub and every spoke is its own Helm release of the nucleus-moodle chart, in its own namespace, with its own database and its own PVC for moodledata.
The control plane provisions tenants by helm install-ing the chart against this cluster. It needs:
- ingress-nginx (or any ingress controller you already run)
- cert-manager (for per-tenant Let's Encrypt — or your existing TLS setup)
- metrics-server (for
kubectl top pod, used by the metrics service) - A
StorageClasswithVolumeSnapshotsupport (needed for backups)
Single-cluster deployments are the default. Multi-region is supported but not yet documented — talk to support if that's your shape.
DNS
Two records:
| Record | Target | Purpose |
|---|---|---|
A cp.your-domain | Control-plane host IP | Operator UI + API |
A *.your-domain | Cluster ingress IP | Tenant subdomains |
Each tenant gets a subdomain under the wildcard.
Why two layers?
The control plane is small, stateful, and rarely restarts. Tenants are large, isolated, and provisioned/destroyed continuously. Splitting them lets each scale and fail independently — a control-plane restart doesn't take tenants down, and a tenant going sideways doesn't take the control plane with it.