Deploy a Multi-Tenant Physical Kafka Cluster

Learn how to deploy a Physical Kafka Cluster (PKC) with Multi-Tenancy enabled using Confluent for Kubernetes (CFK). A platform operator completes this setup once per PKC before onboarding any tenants.

To move the topics of an existing cluster to Logical Kafka Clusters (LKCs), see the Migrate Existing Clusters to Multi-Tenant LKCs page.

Prerequisites

Before you begin, ensure you have the following:

Requirements for a multi-tenant PKC

A multi-tenant PKC has the following requirements:

  • Brokers: At least three. Multi-Tenancy stores LKC metadata in an internal topic that requires three replicas.

  • Authorization: role-based access control (RBAC) authorization with Metadata Service (MDS) on the Kafka and KRaftController CRs.

  • Encryption: TLS on the internal and external listeners.

  • Routing: Server Name Indication (SNI) routing and wildcard DNS records for the LKC hostnames.

  • Deployment topology: A single-region PKC. Multi-Tenancy isn’t supported on multi-region clusters (MRCs) in this release.

For the Kafka CR settings that enforce these requirements, see Step 3.

Step 1: Plan capacity

Before you deploy, establish a broker-capacity baseline from each broker’s JVM heap size. CFK sets the heap to half of the pod memory, up to 32 GiB. Use the baseline to size the broker memory in Step 3: Deploy the PKC with Multi-Tenancy enabled and as the resource ceiling when you calculate tenant quota allocations. The values are per broker:

Capacity dimension

Calculation formula

Producer byte rate

heapGiB × 6.25 MiB/s

Consumer byte rate

producer byte rate × 3

Max connections

heapGiB × 4096

Max connections per tenant

heapGiB × 1024

For example, a broker configured with an 8 GiB JVM heap supports a baseline capacity of 50 MiB/s producer throughput, 150 MiB/s consumer throughput, 32,768 total connections, and 8,192 connections per tenant.

CFK also limits how many new connections per second each LKC and each tenant listener can open on a broker. These limits don’t scale linearly with heap size, so CFK sets them by heap size range:

Broker JVM heap

Per LKC (new connections per second)

Per tenant listener (new connections per second)

4 GiB or less

40

84

More than 4 GiB, up to 16 GiB

50

250

More than 16 GiB

63

750

Step 2: Deploy KRaft controllers

Deploy the KRaft controllers first. The Kafka custom resource (CR) that provisions your PKC references the KRaftController CR, and CFK waits for KRaft to be up before it starts Kafka.

Configure the KRaftController CR with RBAC authorization with spec.authorization.type: rbac and an mdsKafkaCluster dependency. Without RBAC, the controllers run as single-tenant and can’t authorize topic creation in LKCs.

  1. Create a manifest file named kraftcontroller.yaml with the following configuration:

    apiVersion: platform.confluent.io/v1beta1
    kind: KRaftController
    metadata:
      name: <kraftcontroller-name>
      namespace: <your-namespace>
    spec:
      replicas: 3
      image:
        application: confluentinc/cp-server:<cp-version>
        init: confluentinc/confluent-init-container:<cfk-version>
      dataVolumeCapacity: 10G
      configOverrides:
        server:
          - default.replication.factor=3
          - min.insync.replicas=2
      authorization:
        type: rbac
        superUsers:
          - User:<superuser-1>
          - User:<superuser-2>
      listeners:
        controller:
          tls:
            enabled: true
          authentication:
            type: plain
            jaasConfig:
              secretRef: <controller-credential-secret>
      tls:
        autoGeneratedCerts: true
      dependencies:
        mdsKafkaCluster:
          bootstrapEndpoint: <pkc-name>.<your-namespace>.svc.cluster.local:9071
          authentication:
            type: plain
            jaasConfig:
              secretRef: <mds-kafka-credential-secret>
          tls:
            enabled: true
    

    Replace the placeholders with your values:

    • <kraftcontroller-name>: The name of the KRaftController CR.

    • <your-namespace>: Your Kubernetes namespace.

    • <cp-version>: The Confluent Platform version to run, such as 8.4.0.

    • <cfk-version>: The confluent-init-container version that matches your CFK version.

    • <superuser-1> and <superuser-2>: The principals that the controllers treat as super users.

    • <controller-credential-secret> and <mds-kafka-credential-secret>: The names of the secrets that hold the credentials for the controller listener and for the connection to the Kafka cluster that hosts Metadata Service (MDS).

    • <pkc-name>: The name that you give the PKC in the next step. The controllers connect to the PKC’s internal listener on port 9071.

    The controllers assign replicas to new topics, so set default.replication.factor and min.insync.replicas under configOverrides.server on the KRaftController CR. Without them, topics that clients create without a replication factor get a replication factor of 1.

    This example uses plain authentication and auto-generated certificates. Adjust your authentication and TLS settings to match your Kafka configuration. For full configuration options, see Configure and Manage KRaft for Confluent Platform Using Confluent for Kubernetes.

  2. Apply the configuration:

    kubectl apply -f kraftcontroller.yaml
    

Step 3: Deploy the PKC with Multi-Tenancy enabled

Enable Multi-Tenancy on the Kafka CR that provisions your PKC.

CFK rejects a Kafka CR with spec.multiTenant.enabled set to true unless the CR also configures internal and external listener TLS, Metadata Service (MDS), role-based access control (RBAC) authorization, and broker pod memory.

  1. Configure these prerequisites on the same CR, alongside multiTenant:

  2. Ensure these Kafka CR settings are also set. CFK accepts a Kafka CR that omits them, but then reports an error and doesn’t deploy the brokers:

    • Configure spec.dependencies.kafkaRest.authentication. Without it, the CR reports .spec.dependencies.kafkaRest.authentication must be configured.

    • Configure spec.tls. For example, use autoGeneratedCerts: true. Without it, a CR that enables TLS on a listener reports missing .spec.tls. in spec.

  3. Create a manifest file named kafka.yaml with the following configuration:

    apiVersion: platform.confluent.io/v1beta1
    kind: Kafka
    metadata:
      name: <pkc-name>
      namespace: <your-namespace>
    spec:
      replicas: 3
      image:
        application: confluentinc/cp-server:<cp-version>
        init: confluentinc/confluent-init-container:<cfk-version>
      dataVolumeCapacity: 100Gi
      authorization:
        type: rbac
      tls:
        autoGeneratedCerts: true
      services:
        mds:
          tokenKeyPair:
            secretRef: <mds-token-secret>
          provider:
            ldap:
              address: <ldap-address>
              authentication:
                type: simple
                simple:
                  secretRef: <ldap-credentials-secret>
              configurations:
                userNameAttribute: <user-name-attribute>
                userObjectClass: <user-object-class>
                userSearchBase: <user-search-base>
                groupSearchBase: <group-search-base>
          authentication:
            type: bearer
      listeners:
        internal:
          authentication:
            type: plain
            jaasConfig:
              secretRef: <internal-credential-secret>
          tls:
            enabled: true
        external:
          authentication:
            type: plain
            jaasConfig:
              secretRef: <external-credential-secret>
          externalAccess:
            type: loadBalancer
            loadBalancer:
              domain: <external-domain>
          tls:
            enabled: true
      podTemplate:
        resources:
          requests:
            memory: <broker-memory-request>
      multiTenant:
        enabled: true
      dependencies:
        kRaftController:
          clusterRef:
            name: <kraftcontroller-name>
          controllerListener:
            tls:
              enabled: true
            authentication:
              type: plain
              jaasConfig:
                secretRef: <controller-credential-secret>
        kafkaRest:
          authentication:
            type: bearer
            bearer:
              secretRef: <kafka-rest-credential-secret>
    

    Replace the placeholders with your values:

    • <pkc-name>: The name of your PKC.

    • <your-namespace>: Your Kubernetes namespace.

    • <cp-version>: The Confluent Platform version to run, such as 8.4.0.

    • <cfk-version>: The confluent-init-container version that matches your CFK version.

    • <broker-memory-request>: The memory request for each broker pod, such as 8Gi. The broker’s JVM heap is smaller than this value. Use the JVM heap size, not the pod memory request, in the capacity-planning formulas in Step 1: Plan capacity.

    • <kraftcontroller-name>: The name of the KRaftController CR you deployed in Step 2: Deploy KRaft controllers.

    • <internal-credential-secret>: The name of the secret that holds the credentials for the internal listener. The controllers connect to this listener through mdsKafkaCluster, so the secret must include the user that <mds-kafka-credential-secret> in Step 2: Deploy KRaft controllers uses.

    • <external-credential-secret>: The name of the secret that holds the credentials for the external listener. Clients outside your Kubernetes cluster use these credentials to connect to an LKC’s bootstrap URL.

    • <controller-credential-secret>: The name of the secret that holds the credentials for the controller listener. Use the same secret you set in Step 2: Deploy KRaft controllers.

    • <mds-token-secret>: The name of the secret that holds the MDS token key pair.

    • <kafka-rest-credential-secret>: The name of the secret that holds the bearer credentials for the Kafka REST API.

    • <ldap-address>, <ldap-credentials-secret>, <user-name-attribute>, <user-object-class>, <user-search-base>, and <group-search-base>: The connection details and search settings for your LDAP directory. This example uses LDAP. To use a different identity provider, configure provider for that provider instead.

      The search settings must match your directory’s layout. If they don’t, Metadata Service (MDS) can’t search for groups or can’t find users. In that case, Metadata Service (MDS) doesn’t start, or it rejects logins, and the role bindings in Step 7: Configure the RBAC foundation and role hierarchy fail with connection refused or 401 errors. For the LDAP settings and their defaults, see Configure LDAP Group-Based Authorization.

    • <external-domain>: The domain that LKC bootstrap hostnames use, such as mt.example.com.

    clusterRef only tells the brokers which controllers to use. The controllerListener section tells them how to connect to the controller listener, so it must match the listeners.controller settings on the KRaftController CR. Without it, CFK configures the brokers to connect to the controllers over plaintext with no authentication, and the brokers can’t register with the controllers that you configured for TLS and plain authentication in Step 2: Deploy KRaft controllers.

    Without authentication on the external listener, clients outside the cluster can’t connect to an LKC’s bootstrap URL. The connection fails with Unexpected handshake request with client mechanism PLAIN, enabled mechanisms are [].

    By default, CFK doesn’t create Services for LKC hostnames. Configure the DNS rewrite rules described in Configure TLS Certificates and DNS for Multi-Tenant PKCs. Set spec.multiTenant.createTenantNameServices to true only as a fallback when you can’t edit your cluster’s DNS. CFK then creates a Service for every broker of every LKC, which uses up the cluster’s Service IP range as you add LKCs and brokers. For an example of how quickly the number of Services grows, see Configure TLS Certificates and DNS for Multi-Tenant PKCs and Limitations.

    The preceding example is a starting point. Adjust the authentication, TLS certificate, and LDAP settings to match your environment. For full listener, authentication, and MDS configuration options, see Configure Network Encryption for Confluent Platform Using Confluent for Kubernetes and Manage RBAC for Confluent Platform Using Confluent for Kubernetes.

  4. Apply the configuration:

    kubectl apply -f kafka.yaml
    

Caution

Enable Multi-Tenancy when you first deploy the PKC. After any LogicalKafkaCluster CR references this Kafka cluster, CFK prevents you from disabling Multi-Tenancy. To disable it, first decommission every tenant on the PKC.

Step 4: Configure PKC-level settings

Configure the following PKC-wide settings on the Kafka CR before onboarding tenants. These settings are not specific to Multi-Tenancy. Configure them by referring to the documentation that each setting links to:

  • Identity provider: All LKCs on the same PKC share a single identity provider, such as LDAP or OpenID Connect (OIDC). Plan for this shared identity-provider constraint before configuring authentication. For guidance on securing your identity provider, see Plan Security for Confluent for Kubernetes.

  • TLS certificates: Ensure every broker presents a TLS certificate valid for all hosted LKC hostnames. You provision these certificates as part of the TLS approach you choose. For more information, see Manage Certificates.

  • Storage capacity: Choose storage classes and size spec.dataVolumeCapacity to accommodate the total storage footprint of all planned tenants. For more information, see Configure Storage for Confluent Components Using Confluent for Kubernetes.

  • Broker defaults: Set cluster-wide broker configuration under spec.configOverrides.server for the PKC.

Step 5: Configure a TLS approach

Clients reach each LKC through its own bootstrap URL, which SNI routes at the TLS layer. Brokers advertise LKC hostnames with a broker label, such as lkc-<id>-kafka.b0.<domain>. Every broker needs a certificate that’s valid for the LKC hostnames it serves, and the required Subject Alternative Names (SANs) depend on the TLS approach that you choose.

Before you onboard your first tenant, choose your TLS approach and complete its certificate setup. For guidance on choosing an approach and the SANs that each approach requires, see Configure TLS Certificates and DNS for Multi-Tenant PKCs.

The autoGeneratedCerts: true setting in the Step 3: Deploy the PKC with Multi-Tenancy enabled example generates a certificate that covers only in-cluster names. A client outside the cluster then fails the TLS handshake with No subject alternative DNS name matching. To add the wildcard names to the generated certificate, set the platform.confluent.io/managed-cert-add-sans annotation on the Kafka CR when you create it. For example, "*.<external-domain>,*.b0.<external-domain>,*.b1.<external-domain>,*.b2.<external-domain>".

CFK reads this annotation from the Kafka CR only if managedCerts is enabled in the CFK Helm values. Set managedCerts.enabled to true and set managedCerts.caCertificate.secretRef to the secret that holds your certificate authority (CA) certificate and key pair. With the default Helm values, CFK doesn’t read the annotation. For more details, see Manage auto-generated certificates.

Step 6: Verify Confluent Control Center’s auto-detection

If you deploy Confluent Control Center, it requires version 2.7 or later. Set spec.authorization.type to rbac. For deployment instructions, see Install Confluent Platform Using Confluent for Kubernetes.

CFK automatically configures Confluent Control Center for Multi-Tenancy based on the Kafka CR that the ControlCenter CR references. The ControlCenter CR must reference the PKC through spec.dependencies.kafka.discovery. An explicit bootstrapEndpoint doesn’t enable Multi-Tenancy.

Verify that Confluent Control Center detects the PKC as multi-tenant:

kubectl get controlcenter <c3-name> -n <your-namespace> -o jsonpath='{.status.multiTenant}'

A multi-tenant PKC returns {"enabled":true}. If the command returns an empty response, confirm the ControlCenter CR’s Kafka dependency points to the PKC you configured in Step 3: Deploy the PKC with Multi-Tenancy enabled, and that its image is Confluent Control Center 2.7 or later. CFK doesn’t enable Multi-Tenancy on earlier images. For more information, see Multi-tenancy settings.

Step 7: Configure the RBAC foundation and role hierarchy

Multi-Tenancy divides RBAC access across three roles:

  • Platform operator, scoped to the whole PKC. Holds the OrganizationAdmin role.

  • TenantAdmin, scoped to one or more LKCs. Holds the TenantAdmin role, scoped to those LKCs.

  • Developer, scoped to individual resources within an LKC. Holds a standard role, such as DeveloperRead or DeveloperWrite, that the TenantAdmin grants.

For the full breakdown of responsibilities, see Roles and responsibilities.

This step sets up the PKC-wide OrganizationAdmin foundation. Grant the TenantAdmin role to individual users during tenant onboarding. The TenantAdmin then grants developer-level roles within their own LKCs.

  1. A ConfluentRolebinding CR uses the KafkaRestClass CR that spec.kafkaRestClassRef names. If you don’t set spec.kafkaRestClassRef, it uses the KafkaRestClass CR named default. Create a KafkaRestClass CR before you apply the role binding:

    apiVersion: platform.confluent.io/v1beta1
    kind: KafkaRestClass
    metadata:
      name: default
      namespace: <your-namespace>
    spec:
      kafkaRest:
        authentication:
          type: bearer
          bearer:
            secretRef: <rest-credential-secret>
    

    Replace <rest-credential-secret> with the name of the secret that holds the bearer credentials for the Kafka REST API.

  2. Create a manifest file named rolebinding.yaml to assign the OrganizationAdmin role:

    apiVersion: platform.confluent.io/v1beta1
    kind: ConfluentRolebinding
    metadata:
      name: <platform-operator-binding-name>
      namespace: <your-namespace>
    spec:
      principal:
        type: user
        name: <platform-operator-identity>
      role: OrganizationAdmin
      clustersScopeByIds:
        kafkaClusterId: <pkc-cluster-id>
    

    Replace the placeholders with your values:

    • <platform-operator-binding-name>: A name for the ConfluentRolebinding CR.

    • <platform-operator-identity>: The platform operator’s identity in your identity provider.

    • <pkc-cluster-id>: The Kafka cluster ID of the PKC. For instructions on finding cluster IDs, see Cluster Identifiers in Confluent Platform.

  3. Apply the configuration:

    kubectl apply -f rolebinding.yaml
    

The identity that CFK uses to create LKCs, whether through a KafkaRestClass or through a cluster reference with inline authentication details, also needs the OrganizationAdmin role on the PKC. Grant the role the same way as in the preceding example.

For general RBAC CR mechanics, see Manage RBAC for Confluent Platform Using Confluent for Kubernetes.

Verify your deployment

Confirm that all PKC custom resources report a healthy phase before onboarding tenants:

kubectl get kraftcontroller <kraftcontroller-name> -n <your-namespace>
kubectl get kafka <pkc-name> -n <your-namespace>
kubectl get controlcenter <c3-name> -n <your-namespace>

Verify that each command displays RUNNING in the STATUS column.

Confluent Control Center is also required to visualize a multi-tenant PKC’s brokers, LKCs, and per-tenant quota usage, rather than only its CRs’ reconciliation state. A principal with a PKC-scoped role binding, such as one for OrganizationAdmin, can use the following pages:

  • Home page: A PKC-wide summary of resources, including LKCs, topics, partitions, Connect clusters, and ksqlDB clusters. On a newly deployed PKC with no tenants yet, the summary shows zero LKCs.

  • Quota Overview page: After you onboard tenants, quota usage for every LKC, including 95th-percentile (p95) production and consumption rates, active connection counts, partition counts, and quota configuration.