Skip to main content
This page provides a complete, annotated values.yaml reference optimized for production deployments. Copy it, adapt the placeholders to your environment, and deploy.
This is a reference configuration. You must replace all placeholder values (marked with {...}) with your actual infrastructure details before deploying.

How to use


  1. Copy the full configuration below into a file called values-production.yaml
  2. Replace all {...} placeholders with your actual values
  3. Review each section and adjust resource limits for your expected workload
  4. Deploy:
helm install midaz oci://registry-1.docker.io/lerianstudio/midaz-helm \
  --version <version> \
  -n midaz \
  --create-namespace \
  -f values-production.yaml

Full production values


The ACCOUNT_TYPE_VALIDATION and TRANSACTION_ROUTE_VALIDATION environment variables have been deprecated and replaced by the Ledger Settings API. Use PATCH /v1/organizations/{org_id}/ledgers/{ledger_id}/settings to configure accounting validation per ledger.
# =============================================================================
# Midaz Production Values Reference
# =============================================================================
# This file configures Midaz for a production Kubernetes environment with:
# - Unified Ledger service (recommended over legacy onboarding/transaction)
# - External databases (PostgreSQL, MongoDB, RabbitMQ, Valkey/Redis)
# - Ingress with TLS
# - Observability enabled
# - CRM plugin enabled
# - High availability with autoscaling
# =============================================================================

# -- Global name overrides
nameOverride: "midaz"
fullnameOverride: ""
namespaceOverride: "midaz"

# =============================================================================
# GLOBAL: External database bootstrap jobs
# =============================================================================
# These jobs run once on install to create databases, users, and permissions.
# Enable them when using external (non-bundled) databases.

global:
  # -- Bootstrap job for external PostgreSQL
  externalPostgresDefinitions:
    enabled: true
    connection:
      host: "{your-postgres-host}"       # e.g., "prod-postgres.example.com"
      port: "5432"
    postgresAdminLogin:
      # Recommended: use an existing Kubernetes Secret
      useExistingSecret:
        name: "{your-postgres-admin-secret}"  # Must contain DB_USER_ADMIN and DB_ADMIN_PASSWORD keys
      # Alternative: inline credentials (NOT recommended for production)
      # username: "postgres"
      # password: "{your-admin-password}"
    midazCredentials:
      useExistingSecret:
        name: "{your-midaz-credentials-secret}"  # Must contain DB_PASSWORD_MIDAZ key
      # Alternative: inline
      # password: "{your-midaz-password}"

  # -- Bootstrap job for external RabbitMQ
  externalRabbitmqDefinitions:
    enabled: true
    connection:
      protocol: "https"                   # Use "https" for production
      host: "{your-rabbitmq-host}"        # e.g., "prod-rabbitmq.example.com"
      port: "15672"                       # HTTP management port
      portAmqp: "5672"                    # AMQP port
    rabbitmqAdminLogin:
      useExistingSecret:
        name: "{your-rabbitmq-admin-secret}"  # Must contain RABBITMQ_ADMIN_USER and RABBITMQ_ADMIN_PASS
    appCredentials:
      useExistingSecret:
        name: "{your-rabbitmq-app-credentials}"  # Must contain RABBITMQ_DEFAULT_PASS and RABBITMQ_CONSUMER_PASS

# =============================================================================
# LEDGER: Unified service (recommended for new installations)
# =============================================================================
# The Ledger service combines onboarding and transaction into a single
# deployment. This is the recommended approach for all new installations.

ledger:
  enabled: true

  # -- High availability: start with 3 replicas
  replicaCount: 3

  image:
    repository: lerianstudio/midaz-ledger
    pullPolicy: IfNotPresent
    tag: ""  # Defaults to Chart.AppVersion; pin a specific version for production

  # -- Security context (non-root, read-only filesystem)
  securityContext:
    runAsGroup: 1000
    runAsUser: 1000
    runAsNonRoot: true
    capabilities:
      drop:
        - ALL
    readOnlyRootFilesystem: true

  # -- PodDisruptionBudget: ensure at least 2 pods during disruptions
  pdb:
    enabled: true
    minAvailable: 2
    maxUnavailable: 1

  # -- Rolling update with zero downtime
  deploymentUpdate:
    type: RollingUpdate
    maxSurge: 1
    maxUnavailable: 0

  service:
    type: ClusterIP
    port: 3002

  # -- Ingress with TLS
  ingress:
    enabled: true
    className: "{your-ingress-class}"     # e.g., "nginx", "alb", "traefik"
    annotations:
      # NGINX example:
      nginx.ingress.kubernetes.io/proxy-body-size: "10m"
      nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
      # AWS ALB example (uncomment if using ALB):
      # alb.ingress.kubernetes.io/scheme: internal
      # alb.ingress.kubernetes.io/target-type: ip
      # alb.ingress.kubernetes.io/group.name: "midaz"
    hosts:
      - host: "{your-midaz-api-domain}"   # e.g., "api.midaz.example.com"
        paths:
          - path: /
            pathType: Prefix
    tls:
      - secretName: "{your-tls-secret}"   # e.g., "midaz-api-tls"
        hosts:
          - "{your-midaz-api-domain}"

  # -- Resource limits for production
  # Adjust based on your workload. These are starting recommendations.
  resources:
    requests:
      cpu: "1000m"
      memory: "512Mi"
    limits:
      cpu: "2000m"
      memory: "2Gi"

  # -- Autoscaling
  autoscaling:
    enabled: true
    minReplicas: 3
    maxReplicas: 10
    targetCPUUtilizationPercentage: 70
    targetMemoryUtilizationPercentage: 80

  # -- Pod anti-affinity: spread across nodes for high availability
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 100
          podAffinityTerm:
            labelSelector:
              matchExpressions:
                - key: app.kubernetes.io/name
                  operator: In
                  values:
                    - ledger
            topologyKey: kubernetes.io/hostname

  # -- External database configuration
  configmap:
    ENABLE_TELEMETRY: "true"
    # Auth Plugin (enable if using access management)
    PLUGIN_AUTH_ENABLED: "false"
    PLUGIN_AUTH_HOST: ""
    # Accounting Configuration
    # Managed via Ledger Settings API — see PATCH /v1/organizations/{org_id}/ledgers/{ledger_id}/settings
    # Request body: {"accounting": {"validateRoutes": true, "validateAccountType": true}}
    # -- PostgreSQL: Onboarding module
    DB_ONBOARDING_HOST: "{your-postgres-host}"
    DB_ONBOARDING_USER: "midaz"
    DB_ONBOARDING_NAME: "onboarding"
    DB_ONBOARDING_PORT: "5432"
    DB_ONBOARDING_REPLICA_HOST: "{your-postgres-replica-host}"
    DB_ONBOARDING_REPLICA_USER: "midaz"
    DB_ONBOARDING_REPLICA_NAME: "onboarding"
    DB_ONBOARDING_REPLICA_PORT: "5432"
    # -- MongoDB: Onboarding module
    MONGO_ONBOARDING_URI: "mongodb"
    MONGO_ONBOARDING_HOST: "{your-mongodb-host}"
    MONGO_ONBOARDING_NAME: "onboarding"
    MONGO_ONBOARDING_USER: "midaz"
    MONGO_ONBOARDING_PORT: "27017"
    # -- PostgreSQL: Transaction module
    DB_TRANSACTION_HOST: "{your-postgres-host}"
    DB_TRANSACTION_USER: "midaz"
    DB_TRANSACTION_NAME: "transaction"
    DB_TRANSACTION_PORT: "5432"
    DB_TRANSACTION_REPLICA_HOST: "{your-postgres-replica-host}"
    DB_TRANSACTION_REPLICA_USER: "midaz"
    DB_TRANSACTION_REPLICA_NAME: "transaction"
    DB_TRANSACTION_REPLICA_PORT: "5432"
    # -- MongoDB: Transaction module
    MONGO_TRANSACTION_URI: "mongodb"
    MONGO_TRANSACTION_HOST: "{your-mongodb-host}"
    MONGO_TRANSACTION_NAME: "transaction"
    MONGO_TRANSACTION_USER: "midaz"
    MONGO_TRANSACTION_PORT: "27017"
    # -- Redis/Valkey
    REDIS_HOST: "{your-redis-host}:6379"
    # -- RabbitMQ
    RABBITMQ_URI: "amqps"                 # Use "amqps" for TLS, "amqp" without
    RABBITMQ_HOST: "{your-rabbitmq-host}"
    RABBITMQ_PORT_HOST: "5672"
    RABBITMQ_PORT_AMQP: "15672"
    RABBITMQ_DEFAULT_USER: "transaction"
    RABBITMQ_CONSUMER_USER: "consumer"
    RABBITMQ_TRANSACTION_ASYNC: "false"
    # -- Audit
    AUDIT_LOG_ENABLED: "true"             # Enable audit logging for production
    # -- Balance Sync Worker
    BALANCE_SYNC_BATCH_SIZE: "100"
    BALANCE_SYNC_FLUSH_TIMEOUT_MS: "1000"
    BALANCE_SYNC_POLL_INTERVAL_MS: "500"

  # -- Use existing Kubernetes Secrets (recommended)
  useExistingSecret: true
  existingSecretName: "midaz-ledger"
  # Create this secret manually before deploying:
  # kubectl create secret generic midaz-ledger \
  #   --from-literal=DB_ONBOARDING_PASSWORD='{password}' \
  #   --from-literal=DB_ONBOARDING_REPLICA_PASSWORD='{password}' \
  #   --from-literal=MONGO_ONBOARDING_PASSWORD='{password}' \
  #   --from-literal=DB_TRANSACTION_PASSWORD='{password}' \
  #   --from-literal=DB_TRANSACTION_REPLICA_PASSWORD='{password}' \
  #   --from-literal=MONGO_TRANSACTION_PASSWORD='{password}' \
  #   --from-literal=REDIS_PASSWORD='{password}' \
  #   --from-literal=RABBITMQ_DEFAULT_PASS='{password}' \
  #   --from-literal=RABBITMQ_CONSUMER_PASS='{password}' \
  #   -n midaz

  serviceAccount:
    create: true
    annotations: {}

# =============================================================================
# LEGACY SERVICES: Disabled when using Ledger
# =============================================================================

onboarding:
  enabled: false

transaction:
  enabled: false

# =============================================================================
# CRM: Customer Relationship Management
# =============================================================================

crm:
  enabled: true

  replicaCount: 2

  image:
    repository: lerianstudio/midaz-crm
    pullPolicy: IfNotPresent
    tag: ""  # Defaults to Chart.AppVersion

  securityContext:
    runAsGroup: 1000
    runAsUser: 1000
    runAsNonRoot: true
    capabilities:
      drop:
        - ALL
    readOnlyRootFilesystem: true

  pdb:
    enabled: true
    minAvailable: 1
    maxUnavailable: 1

  service:
    type: ClusterIP
    port: 4003

  ingress:
    enabled: true
    className: "{your-ingress-class}"
    hosts:
      - host: "{your-crm-api-domain}"     # e.g., "crm.midaz.example.com"
        paths:
          - path: /
            pathType: Prefix
    tls:
      - secretName: "{your-crm-tls-secret}"
        hosts:
          - "{your-crm-api-domain}"

  resources:
    requests:
      cpu: "200m"
      memory: "256Mi"
    limits:
      cpu: "500m"
      memory: "512Mi"

  autoscaling:
    enabled: true
    minReplicas: 2
    maxReplicas: 5
    targetCPUUtilizationPercentage: 80
    targetMemoryUtilizationPercentage: 80

  configmap:
    ENV_NAME: "production"
    PLUGIN_AUTH_ENABLED: "false"
    PLUGIN_AUTH_ADDRESS: ""
    MONGO_HOST: "{your-mongodb-host}"
    MONGO_NAME: "crm"
    MONGO_PORT: "27017"
    MONGO_USER: "midaz"

  useExistingSecret: true
  existingSecretName: "midaz-crm"
  # Create this secret manually before deploying:
  # kubectl create secret generic midaz-crm \
  #   --from-literal=LCRYPTO_HASH_SECRET_KEY='{generate-a-random-64-hex-string}' \
  #   --from-literal=LCRYPTO_ENCRYPT_SECRET_KEY='{generate-a-random-64-hex-string}' \
  #   --from-literal=MONGO_PASSWORD='{password}' \
  #   -n midaz

# =============================================================================
# BUNDLED DEPENDENCIES: All disabled (using external databases)
# =============================================================================
# In production, use managed database services (AWS RDS, Atlas, etc.)
# instead of bundled containers.

postgresql:
  enabled: false

mongodb:
  enabled: false

rabbitmq:
  enabled: false

valkey:
  enabled: false

# =============================================================================
# OBSERVABILITY
# =============================================================================

grafana:
  enabled: true
  name: grafana

  ingress:
    enabled: true
    className: "{your-ingress-class}"
    annotations: {}
    hosts:
      - host: "{your-grafana-domain}"     # e.g., "grafana.midaz.example.com"
        paths:
          - path: /
            pathType: Prefix
    tls:
      - secretName: "{your-grafana-tls-secret}"
        hosts:
          - "{your-grafana-domain}"

# -- OpenTelemetry Collector (send telemetry to Lerian)
otel-collector-lerian:
  enabled: true
  external: false

  opentelemetry-collector:
    config:
      processors:
        resource/add_client_id:
          attributes:
            - key: client.id
              value: "{your-company-name}"  # Your company name for telemetry identification
              action: upsert

  extraEnvs:
    - name: OTEL_API_KEY
      valueFrom:
        secretKeyRef:
          name: otel-api-key
          key: api-key
  # Create this secret:
  # kubectl create secret generic otel-api-key \
  #   --from-literal=api-key='{your-otel-api-key}' \
  #   -n midaz

  exporters:
    otlphttp/server:
      endpoint: "https://telemetry.lerian.io:443"
      headers:
        x-api-key: "${OTEL_API_KEY}"

Key decisions explained


Why Ledger instead of Onboarding + Transaction?

The unified ledger service is the recommended approach for all new installations. It combines both modules into a single deployment, reducing operational overhead:
  • Fewer pods to manage and monitor
  • Simplified configuration (single configmap/secret)
  • Single ingress endpoint
  • Better resource utilization
  • Will become the only option in future releases

Why external databases?

Production deployments should use managed database services for:
  • Automated backups and point-in-time recovery
  • High availability with automatic failover
  • Monitoring and alerting built-in
  • Scaling without application downtime
  • Security patching managed by the provider
Recommended managed services:
DependencyAWSGCPAzure
PostgreSQLAmazon RDSCloud SQLAzure Database for PostgreSQL
MongoDBAmazon DocumentDB or MongoDB AtlasMongoDB AtlasAzure Cosmos DB (MongoDB API)
RabbitMQAmazon MQSelf-managed on GKESelf-managed on AKS
Redis/ValkeyAmazon ElastiCacheMemorystoreAzure Cache for Redis

Why use Kubernetes Secrets instead of inline passwords?

Inline passwords in values.yaml are visible in Helm release history and may be committed to version control. Using useExistingSecret: true with pre-created Kubernetes Secrets:
  • Keeps credentials out of Helm values
  • Allows rotation without Helm upgrade
  • Integrates with secret management tools (Vault, External Secrets Operator, Sealed Secrets)

Pod anti-affinity

The podAntiAffinity rule distributes Ledger pods across different nodes, so a single node failure doesn’t bring down the entire service.

Checklist before deploying


1

Create Kubernetes Secrets

Create all required secrets in the midaz namespace before running helm install:
  • midaz-ledger (database and message broker credentials)
  • midaz-crm (encryption keys and MongoDB password)
  • otel-api-key (if using Lerian telemetry)
2

Configure DNS

Point your domains to the ingress controller’s external IP:
  • {your-midaz-api-domain} → ingress IP
  • {your-crm-api-domain} → ingress IP
  • {your-grafana-domain} → ingress IP
3

Provision TLS certificates

Either use cert-manager for automatic certificate provisioning or manually create TLS secrets for each domain.
4

Verify database connectivity

Ensure your Kubernetes cluster can reach all external databases. Test with:
kubectl run test-pg --rm -it --image=postgres:17 -- \
  psql -h {your-postgres-host} -U midaz -d onboarding -c "SELECT 1"
5

Review resource limits

Adjust CPU and memory limits based on your expected transaction volume. The values in this reference are starting points for moderate workloads.
6

Deploy

helm install midaz oci://registry-1.docker.io/lerianstudio/midaz-helm \
  --version <version> \
  -n midaz \
  --create-namespace \
  -f values-production.yaml
7

Verify deployment

kubectl get pods -n midaz -o wide
kubectl get ingress -n midaz
helm list -n midaz