Terraform module for deploying OpenObserve on Kubernetes using the official Helm chart.
OpenObserve is a cloud-native observability platform for logs, metrics, traces, dashboards, RUM, error tracking, and session replay — with Elasticsearch API compatibility.
Helm chart: openobserve/openobserve-helm-chart v0.80.3
module "openobserve" {
source = "openobserve/openobserve/kubernetes"
version = "~> 1.0"
auth = {
root_user_email = "admin@example.com"
root_user_password = "ChangeMe123!"
}
# Single-node local mode: no PostgreSQL or NATS required
meta_store = "sqlite"
cluster_coordinator = "local"
queue_store = "local"
nats = { enabled = false }
}Access the UI:
kubectl port-forward -n openobserve svc/openobserve-router 5080:5080
# Open http://localhost:5080module "openobserve" {
source = "openobserve/openobserve/kubernetes"
version = "~> 1.0"
auth = {
root_user_email = var.root_user_email
root_user_password = var.root_user_password
postgres_dsn = var.postgres_dsn
s3_access_key = var.s3_access_key # omit to use IRSA
s3_secret_key = var.s3_secret_key
}
replica_count = {
ingester = 3
querier = 2
router = 2
compactor = 1
alertmanager = 1
}
meta_store = "postgres"
cluster_coordinator = "nats"
queue_store = "nats"
s3 = {
provider = "s3"
region = "us-east-1"
bucket_name = "my-openobserve-data"
}
ingress = {
enabled = true
class_name = "nginx"
host = "openobserve.example.com"
tls_secret_name = "openobserve-tls"
annotations = {
"cert-manager.io/cluster-issuer" = "letsencrypt-prod"
}
}
persistence = {
ingester = { size = "100Gi", storage_class = "gp3" }
querier = { size = "100Gi", storage_class = "gp3" }
alertmanager = { size = "10Gi", storage_class = "gp3" }
}
resources = {
ingester = {
requests = { memory = "2Gi", cpu = "500m" }
limits = { memory = "8Gi", cpu = "2000m" }
}
querier = {
requests = { memory = "2Gi", cpu = "500m" }
limits = { memory = "8Gi", cpu = "2000m" }
}
}
nats = { enabled = true }
minio = { enabled = false }
}module "openobserve" {
source = "openobserve/openobserve/kubernetes"
version = "~> 1.0"
image = {
repository = "o2cr.ai/openobserve/openobserve-enterprise"
tag = "v0.80.2"
}
# ... rest of your configuration
}Any setting not exposed as a first-class variable can be passed through extra_values (highest precedence):
module "openobserve" {
source = "openobserve/openobserve/kubernetes"
version = "~> 1.0"
# ... required variables
extra_values = [<<-EOT
enterprise:
enabled: true
config:
ZO_SWAGGER_ENABLED: "true"
ZO_PROMETHEUS_ENABLED: "true"
EOT
]
}| Example | Description |
|---|---|
| examples/minimal | Single-node SQLite deployment for development |
| examples/complete | Production HA: PostgreSQL + NATS + S3 + Ingress + TLS |
This module is published at:
registry.terraform.io/modules/openobserve/openobserve/kubernetes
| Requirement | Version | Notes |
|---|---|---|
| Terraform | >= 1.9 |
optional() with defaults requires 1.3+; mock provider tests require 1.7+ |
| hashicorp/helm provider | ~> 2.16 |
|
| Kubernetes cluster | >= 1.25 |
EKS, GKE, AKS, or self-managed |
| Default StorageClass | — | Required for persistent volumes |
| PostgreSQL | >= 14 |
Required for meta_store = "postgres" (HA) |
| S3-compatible bucket | — | Required for production data persistence |
┌─────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
Ingest / Query ───►│ router (stateless, horizontally scalable) │
│ │ │ │
│ ingester ◄────► querier │
│ (WAL + disk) (disk cache) │
│ │ │ │
│ compactor alertmanager │
│ │ │
│ NATS (bundled or external) │
│ │ │
└──────┼─────────────────────────────────────-─┘
│
┌───────────┼──────────────┐
│ │ │
PostgreSQL S3 Object Store
(metadata) (long-term data)
- Check the OpenObserve Helm chart releases for breaking changes.
- Update
chart_versionin your module call. - Run
terraform planand review the diff before applying. - For major chart version bumps, run
terraform applyduring a maintenance window.
| Name | Version |
|---|---|
| terraform | >= 1.9 |
| aws | ~> 5.0 |
| helm | ~> 2.16 |
| kubernetes | ~> 2.35 |
| Name | Version |
|---|---|
| helm | ~> 2.16 |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| affinity | Pod affinity/anti-affinity rules per component. | object({ |
{} |
no |
| atomic | Automatically roll back the release on install/upgrade failure. | bool |
false |
no |
| auth | Authentication credentials. All values are stored in a Kubernetes Secret. root_user_email and root_user_password are required. Provide s3_access_key / s3_secret_key for AWS-signature S3 auth. Provide postgres_dsn for PostgreSQL metadata store. |
object({ |
n/a | yes |
| aws_config | AWS infrastructure settings. Only used when create_aws_infrastructure = true. | object({ |
{} |
no |
| capacity | Capacity planning inputs. When set, the module computes and outputs: - recommended deployment mode (single-node vs HA) - recommended replica counts per component - recommended EKS instance type and node count - S3 storage estimate - estimated monthly cost These are informational outputs only; set replica_count explicitly to override the recommendations. When create_aws_infrastructure = true, the recommendations are used as defaults for aws_config if not overridden. Reference data (256 GB/day): Single-node: 5 cores, ~$179/month HA: 25 cores, ~$927/month |
object({ |
{} |
no |
| chart_version | Version of the openobserve Helm chart. Pin this for reproducible deployments. | string |
"0.80.3" |
no |
| cleanup_on_fail | Delete newly created resources when an upgrade fails. | bool |
false |
no |
| cluster_coordinator | Cluster coordination backend. 'nats' is required for multi-node deployments. | string |
"nats" |
no |
| config | Raw ZO_* environment variable overrides merged on top of module-managed config. Use for settings not exposed as first-class variables. Example: { "ZO_HTTP_WORKER_NUM" = "8", "ZO_QUERY_TIMEOUT" = "300" } |
map(string) |
{} |
no |
| create_aws_infrastructure | When true, the module creates a complete AWS environment (VPC, EKS cluster, S3 bucket, IAM role with IRSA) before deploying OpenObserve into it. When false (default), OpenObserve is deployed into your existing cluster; the aws_config block is ignored and no AWS resources are created. |
bool |
false |
no |
| create_namespace | Create the Kubernetes namespace if it does not exist. | bool |
true |
no |
| data_retention_days | Days to retain data before compaction removes it (ZO_COMPACT_DATA_RETENTION_DAYS). | number |
3650 |
no |
| extra_values | List of raw YAML value strings merged last (highest precedence). Use for helm chart sections not exposed as variables. Example: [<<-EOT enterprise: enabled: true EOT] |
list(string) |
[] |
no |
| image | Container image configuration. Set repository to 'o2cr.ai/openobserve/openobserve-enterprise' for the enterprise edition. Leave tag empty to use the chart's default version. |
object({ |
{} |
no |
| image_pull_secrets | List of Kubernetes Secret names used to pull the container image. | list(string) |
[] |
no |
| ingress | Ingress configuration. Enable to expose OpenObserve externally. Requires an Ingress controller (e.g. nginx-ingress) in the cluster. Set tls_secret_name to enable HTTPS with cert-manager. |
object({ |
{} |
no |
| meta_store | Metadata storage backend. Use 'postgres' for all HA deployments. | string |
"postgres" |
no |
| minio | MinIO dependency bundled with the chart. Disable when using AWS S3 or an external MinIO instance. | object({ |
{} |
no |
| namespace | Kubernetes namespace to deploy OpenObserve into. | string |
"openobserve" |
no |
| nats | NATS dependency bundled with the chart. Disable when using an external NATS cluster. | object({ |
{} |
no |
| node_selector | Node selector labels per component. Keys follow the per-component pattern used by the chart (ingester, querier, router, compactor, alertmanager). | object({ |
{} |
no |
| persistence | Persistent volume configuration per component. Storage class defaults to the cluster default when empty. | object({ |
{} |
no |
| queue_store | Distributed queue backend. 'nats' is required for multi-node deployments. | string |
"nats" |
no |
| release_name | Name of the Helm release. | string |
"openobserve" |
no |
| replica_count | Number of replicas per component. Increase querier/ingester for HA; router is stateless and scales horizontally. | object({ |
{} |
no |
| resources | CPU/memory resource requests and limits per component. Example: resources = { ingester = { requests = { memory = "2Gi", cpu = "500m" }, limits = { memory = "8Gi", cpu = "2000m" } } querier = { requests = { memory = "2Gi", cpu = "500m" }, limits = { memory = "8Gi" } } } |
any |
{} |
no |
| s3 | S3-compatible object storage configuration. OpenObserve uses S3 for long-term data persistence. Set server_url to use MinIO or other S3-compatible providers. |
object({ |
{} |
no |
| service | Kubernetes Service configuration for the router component. | object({ |
{} |
no |
| timeout | Timeout in seconds for Helm install/upgrade operations. | number |
600 |
no |
| tolerations | Pod tolerations per component. Each element is a Kubernetes toleration object. | object({ |
{} |
no |
| wait | Wait for all pods and services to be ready before marking the release as successful. | bool |
true |
no |
| Name | Description |
|---|---|
| app_version | Application version reported by the Helm chart metadata. |
| aws_infrastructure | AWS resource details created by the module. Null when create_aws_infrastructure = false. |
| capacity_recommendations | Capacity planning recommendations derived from your ingestion_gb_per_day input. Use these to right-size replicas, EKS nodes, and plan AWS spend before applying. Set capacity.ingestion_gb_per_day to enable. |
| chart_version | Version of the deployed Helm chart. |
| grpc_endpoint | In-cluster gRPC endpoint used by OpenTelemetry exporters. |
| http_endpoint | In-cluster HTTP endpoint for the OpenObserve UI and ingestion API. |
| ingress_host | Ingress hostname, or empty string when ingress is disabled. |
| namespace | Kubernetes namespace where OpenObserve is deployed. |
| release_name | Name of the deployed Helm release. |
| service_name | Kubernetes Service name for the OpenObserve router (entry point for all traffic). |
| status | Current Helm release status (e.g. deployed, failed). |
See CONTRIBUTING.md.
Apache 2.0 — see LICENSE.