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-tenantTenantAn isolated organizational partition that owns Data pools, Datasets, Users, Groups, and Roles. All access rules exist within their Tenant, and the Tenant is the widest Scope of a Role. Currently, one Tenant corresponds to the Platform. 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 (CloudNativePGCloudNativePGA Kubernetes operator for running and managing PostgreSQL clusters. In CIVITAS/CORE it is used to provision and operate Postgres databases. for PostgreSQL, Strimzi for KafkaApache KafkaA distributed event streaming platform. In CIVITAS/CORE it is used as the message bus to transport events, models and data in data flows.) 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 (KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs., APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs., Postgres clusters, KafkaApache KafkaA distributed event streaming platform. In CIVITAS/CORE it is used as the message bus to transport events, models and data in data flows. clusters, Portal, …) into a dedicated namespace named after the instance (
global.instanceSlug). Each instance gets its own KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. 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 (CloudNativePGCloudNativePGA Kubernetes operator for running and managing PostgreSQL clusters. In CIVITAS/CORE it is used to provision and operate Postgres databases.) and Kafka (Strimzi) custom resources in every instance namespace — each instance still runs its own isolated Postgres cluster and KafkaApache KafkaA distributed event streaming platform. In CIVITAS/CORE it is used as the message bus to transport events, models and data in data flows. 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-tenantTenantAn isolated organizational partition that owns Data pools, Datasets, Users, Groups, and Roles. All access rules exist within their Tenant, and the Tenant is the widest Scope of a Role. Currently, one Tenant corresponds to the Platform. 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.