How to deploy via Terraform¶
This guide describes how to deploy Charmed Apache Kafka using Terraform and the Juju Terraform provider.
For Juju CLI-based deployment, see the Juju CLI deployment guide.
Prerequisites¶
A Juju controller bootstrapped on the target VM or Kubernetes cloud (see Juju CLI deployment guide for setup instructions)
A Juju model created on the controller
Terraform (
>= 1.7.3) installed
Terraform configuration¶
Select the module matching the Juju model substrate. The VM module and K8s module have the same deployment profile structure; K8s additionally supports an ingress offer for Kafka UI.
The same main.tf is used for both production and testing deployments — the deployment mode is
controlled via a kafka.auto.tfvars file.
See main.tf
terraform {
required_providers {
juju = {
source = "juju/juju"
version = ">= 1.0.0"
}
}
}
provider "juju" {}
variable "model_name" {
description = "Name of the Juju model to deploy to"
type = string
}
variable "model_owner" {
description = "Owner of the Juju model"
type = string
default = "admin"
}
variable "profile" {
description = "Deployment profile: 'production' or 'testing'"
type = string
default = "testing"
}
variable "broker" {
description = "Apache Kafka broker configuration"
default = {}
}
variable "controller" {
description = "Apache Kafka KRaft controller configuration"
default = {}
}
variable "integrator" {
description = "Data Integrator configuration for admin user creation"
default = { units = 0 }
}
variable "connect" {
description = "Kafka Connect configuration"
default = { units = 0 }
}
variable "karapace" {
description = "Karapace Schema Registry configuration"
default = { units = 0 }
}
variable "ui" {
description = "Kafbat Kafka UI configuration"
default = { units = 0 }
}
variable "tls_offer" {
description = "TLS provider cross-model offer URL"
type = string
default = null
}
variable "cos_offers" {
description = "COS cross-model offer URLs for observability"
default = {}
}
variable "ingress_offer" {
description = "Ingress provider cross-model offer URL for Kafka UI (Kubernetes)"
type = string
default = null
}
data "juju_model" "kafka" {
name = var.model_name
owner = var.model_owner
}
module "kafka" {
# Select exactly one source for the target substrate:
# VM: git::https://github.com/canonical/kafka-bundle//terraform?ref=main
# K8s: git::https://github.com/canonical/kafka-k8s-bundle//terraform?ref=main
source = "<substrate-module-source>"
model_uuid = data.juju_model.kafka.uuid
profile = var.profile
broker = var.broker
controller = var.controller
integrator = var.integrator
connect = var.connect
karapace = var.karapace
ui = var.ui
tls_offer = var.tls_offer
cos_offers = var.cos_offers
# Kubernetes: enable when using the K8s module.
# ingress_offer = var.ingress_offer
}
When controller includes units > 0, the module deploys separate broker and controller
applications. When controller has units = 0, the broker co-locates both the broker and
controller roles in a single application.
Deploy for production¶
For production use, deploy separate kafka (broker) and controller (KRaft controller)
applications and integrate them. To maintain high availability, 3+ broker units and 3 or 5
controller units are recommended.
Save the following as kafka.auto.tfvars. The application names are arbitrary; the examples use
conventional names for each substrate:
model_name = "terraform"
profile = "production"
broker = {
app_name = "kafka"
channel = "4/stable"
units = 3
}
controller = {
app_name = "controller"
channel = "4/stable"
units = 3
}
model_name = "terraform"
profile = "production"
broker = {
app_name = "kafka-k8s"
channel = "4/stable"
units = 3
}
controller = {
app_name = "controller"
channel = "4/stable"
units = 3
}
(Alternative) Deploy for testing¶
For non-production testing clusters, co-locate both KRaft controller and broker services in a single application to save resources.
Save the following as kafka.auto.tfvars:
model_name = "terraform"
broker = {
app_name = "kafka"
channel = "4/stable"
units = 3
}
controller = {
units = 0
}
model_name = "terraform"
broker = {
app_name = "kafka-k8s"
channel = "4/stable"
units = 3
}
controller = {
units = 0
}
With controller.units = 0, the controller role is co-located within the broker application. The
profile defaults to "testing".
Deploy¶
Initialise Terraform, then preview and apply the deployment:
terraform init
terraform plan
Review the plan output, then apply:
terraform apply
Terraform automatically loads the .auto.tfvars file in the working directory. See the profile
reference for VM or
K8s.
Wait for Terraform to finish. Then, monitor the Juju model status with:
watch juju status --color
The deployment is complete once all units show active and idle status.
(Optional) Create an external admin user¶
After deployment, the Apache Kafka cluster does not expose any external listeners by default. To
create an admin user, add the following integrator block to your kafka.auto.tfvars file:
integrator = {
app_name = "data-integrator"
channel = "latest/stable"
config = {
topic-name = "__admin-user"
extra-user-roles = "admin"
}
units = 1
}
The Data Integrator is configured with admin role, granting super.user permissions on the
cluster. The bundle automatically integrates it with the Kafka broker.
Apply the changes:
terraform apply
Retrieve authentication credentials with:
juju run data-integrator/leader get-credentials
(Optional) Enable TLS encryption¶
To encrypt client-facing traffic, pass a
cross-model offer
URL from an existing TLS provider (e.g. self-signed-certificates) to the module. Add the following
to your kafka.auto.tfvars file:
tls_offer = "<controller>:<owner>/<model>.certificates"
The module will integrate all Kafka applications with the TLS provider automatically.
Enable ingress for Kafka UI on Kubernetes¶
When deploying Kafka UI on K8s, provide an ingress offer:
ingress_offer = "<controller>:<owner>/<model>.ingress"
Remove the comment marker from the ingress_offer = var.ingress_offer module argument in main.tf.
The VM module does not accept or require this argument.
(Optional) Enable observability with COS¶
To connect the cluster to the
Canonical Observability Stack (COS), provide the
three required cross-model offer URLs. Add the following to your kafka.auto.tfvars file:
cos_offers = {
dashboard = "<controller>:<owner>/<cos-model>.grafana-dashboards"
metrics = "<controller>:<owner>/<cos-model>.prometheus-receive-remote-write"
logging = "<controller>:<owner>/<cos-model>.loki-logging"
}
All three fields must be set together — the module validates that either all or none are provided.
The metrics offer must be the Prometheus receive-remote-write endpoint: the module deploys an
opentelemetry-collector that scrapes Kafka metrics and forwards them to COS via remote write.
Terraform module reference¶
See the Terraform module reference for the full list of input variables and outputs exposed by the Charmed Apache Kafka Terraform module.