Deployment Concept
Before you configure or deploy anything, make two independent, combinable decisions about how CIVITAS/CORE V2 lays out on your cluster. Both shape your namespace structure, RBAC, DNS planning and how upgrades roll out — and both are much easier to get right up front than to change later.
- How many instances share the cluster? — one installation per cluster, or several with shared operators.
- Namespace layout per instance — all components in one namespace, or one namespace per component.
This page explains the trade-offs and advises on each. For the actual settings see the Configuration guide; for the deploy commands see the Deployment guide.
How many instances per cluster?
CIVITAS/CORE supports two deployment topologies on a cluster:
| Topology | Operators | When to use |
|---|---|---|
| Single instance per cluster | Deployed together with the instance (all-in-one) | Development clusters, dedicated single-tenant production clusters — anything that runs exactly one instance |
| Multiple instances per cluster | Deployed once per cluster, shared by all instances | One cluster hosting several independent installations (e.g. multiple clients, or staging + production) |
If there is any chance you will eventually run more than one instance on a cluster, start with the two-layer model below from the beginning. Splitting an already-deployed single instance into the shared-operator layout afterwards is more work than setting it up correctly up front.
The two-layer model
For multi-instance deployments, the deployment is split into two layers:
- Operator layer — deployed once per cluster. Installs the cluster operators (CloudNativePG for PostgreSQL, Strimzi for Kafka) plus their CRDs and cluster-wide RBAC into a fixed, instance-independent namespace (
civitas-operatorsby default), and the cluster-scoped runtime Kyverno policies (runtime-policies). The operators watch all namespaces, so a single deployment reconciles every instance on the cluster. - Instance layer — deployed once per instance, any number of times. Installs everything else (Keycloak, APISIX, Postgres clusters, Kafka clusters, Portal, …) into a dedicated namespace named after the instance (
global.instanceSlug). Each instance gets its own Keycloak realm and its own subdomains and is fully isolated from the others.
Cluster
├── civitas-operators # operator layer (deployed ONCE)
│ ├── cloudnative-pg # watches all namespaces (clusterWide)
│ ├── strimzi-kafka-operator # watches all namespaces (watchAnyNamespace)
│ └── runtime-policies # cluster-scoped Kyverno ClusterPolicies (once per cluster)
├── <instanceSlug-a> # instance layer (per instance)
│ ├── postgres-cluster, kafka-cluster, keycloak, apisix, portal, …
└── <instanceSlug-b>
└── postgres-cluster, kafka-cluster, keycloak, apisix, portal, …
The shared operators reconcile the Cluster (CloudNativePG) and Kafka (Strimzi) custom resources in every instance namespace — each instance still runs its own isolated Postgres cluster and Kafka cluster. Only the cluster-scoped pieces (the operators with their CRDs and cluster-wide RBAC, and the runtime ClusterPolicy objects) are shared.
Because these shared pieces are cluster-scoped, they are deployed and upgraded once per cluster. The Deployment guide walks through the operator/instance flow; the Configuration guide covers instanceSlug and the operator namespace.
Namespace layout per instance
Independently of how many instances share the cluster, each instance can run all its components in one namespace or give each component its own. This is controlled by global.singleNamespace:
| Model | Setting | When to use it |
|---|---|---|
| Single namespace | global.singleNamespace: true (default) | Local development, demos, small clusters, or shared-tenant restrictions where you only get one namespace. |
| Multi namespace | global.singleNamespace: false | Production and staging. Gives per-component isolation, clearer RBAC boundaries, and stricter network policies. |
Namespaces are derived from global.instanceSlug and the component name:
- Single namespace: everything lands in
<instanceSlug>(e.g.dev). - Multi namespace: each component gets
<instanceSlug>-<component>(e.g.dev-postgres,dev-keycloak,dev-apisix).
In multi-namespace mode the component suffix is appended to instanceSlug, so the combined name must stay within Kubernetes' 63-character limit — keep instanceSlug short.
Because everything is derived from instanceSlug, switching between the two models is a single configuration flag — you never rename namespaces by hand, and cross-namespace references that use component names keep working in both models. Set it in the Configuration guide. The per-component wiring (namespace overrides, cross-namespace references) is covered in Component Configuration.
How the two decisions combine
The two choices are orthogonal and combine freely:
- How many instances is about running the whole platform many times on one cluster behind one shared set of operators; each instance is keyed by its own
instanceSlug. - Namespace layout is about how a single instance spreads its own components across namespaces.
So a single instance can still spread across many namespaces, and several instances can each run single-namespace — pick each independently. Neither choice constrains the other.