MATIH Platform is in active MVP development. Documentation reflects current implementation status.
17. Kubernetes & Helm
Umbrella Charts (Legacy)

Umbrella Charts

MATIH uses umbrella charts to orchestrate the deployment of multiple related services as a single Helm release. The two primary umbrella charts -- matih-control-plane and matih-data-plane -- aggregate all services within their respective architectural tiers, enabling coordinated deployments, shared dependency management, and consistent value propagation.


What Are Umbrella Charts?

An umbrella chart is a Helm chart whose primary purpose is to declare dependencies on other charts (subcharts) and coordinate their deployment. The umbrella chart itself typically contains minimal templates of its own -- its value lies in aggregating subcharts and providing a unified values surface.

matih-control-plane/           # Umbrella chart
  Chart.yaml                   # Declares 10+ service subcharts
  values.yaml                  # Unified values for all subcharts
  values-auth.yaml             # Auth-specific overrides
  templates/                   # Minimal templates (namespace, RBAC)
    _helpers.tpl

matih-control-plane Umbrella Chart

The control plane umbrella chart deploys all services that manage the multi-tenant platform.

Chart.yaml

apiVersion: v2
name: matih-control-plane
description: Matih Platform - Control Plane Services
type: application
version: 1.0.0
appVersion: "1.0.0"
 
keywords:
  - matih
  - control-plane
  - multi-tenant
  - enterprise
 
home: https://github.com/matih/matih-platform
sources:
  - https://github.com/matih/matih-platform
 
maintainers:
  - name: Matih Platform Team
    email: platform@matih.ai
 
annotations:
  artifacthub.io/license: Apache-2.0
  artifacthub.io/category: platform
 
dependencies:
  # Application services
  - name: iam-service
    version: "1.x.x"
    repository: "file://../iam-service"
    condition: iam-service.enabled
 
  - name: tenant-service
    version: "1.x.x"
    repository: "file://../tenant-service"
    condition: tenant-service.enabled
 
  - name: config-service
    version: "1.x.x"
    repository: "file://../config-service"
    condition: config-service.enabled
 
  - name: audit-service
    version: "1.x.x"
    repository: "file://../audit-service"
    condition: audit-service.enabled
 
  - name: notification-service
    version: "1.x.x"
    repository: "file://../notification-service"
    condition: notification-service.enabled
 
  # Shared infrastructure
  - name: postgresql
    version: "13.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled
 
  - name: redis
    version: "18.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled
 
  - name: kafka
    version: "26.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: kafka.enabled

Dependency Types

The control plane umbrella references two types of dependencies:

TypeRepositoryExamplePurpose
Local filefile://../<service>iam-service, tenant-serviceMATIH application services
Remote registryhttps://charts.bitnami.com/bitnamipostgresql, redis, kafkaThird-party infrastructure

Local file references use relative paths from the umbrella chart directory. This means the umbrella and its subchart directories must be siblings:

infrastructure/helm/
  matih-control-plane/    # Umbrella chart
  iam-service/            # Subchart (file://../iam-service)
  tenant-service/         # Subchart (file://../tenant-service)
  config-service/         # Subchart (file://../config-service)
  ...

Control Plane Values

The umbrella chart's values.yaml controls which subcharts are enabled and provides service-level overrides:

# Enable/disable individual services
iam-service:
  enabled: true
  replicaCount: 2
  image:
    tag: ""  # Set by CD pipeline
 
tenant-service:
  enabled: true
  replicaCount: 2
  image:
    tag: ""
 
config-service:
  enabled: true
  replicaCount: 2
  image:
    tag: ""
 
audit-service:
  enabled: true
  replicaCount: 2
  image:
    tag: ""
 
notification-service:
  enabled: true
  replicaCount: 2
  image:
    tag: ""
 
# Shared PostgreSQL for control plane
postgresql:
  enabled: true
  auth:
    existingSecret: "matih-cp-postgresql"
    secretKeys:
      adminPasswordKey: "postgres-password"
  primary:
    persistence:
      enabled: true
      size: 50Gi
      storageClass: matih-premium-ssd
    resources:
      requests:
        cpu: 500m
        memory: 1Gi
      limits:
        cpu: 2000m
        memory: 4Gi
 
# Shared Redis for control plane
redis:
  enabled: true
  architecture: standalone
  auth:
    enabled: true
    existingSecret: "matih-cp-redis"
  master:
    persistence:
      enabled: true
      size: 10Gi
    resources:
      requests:
        cpu: 100m
        memory: 256Mi
      limits:
        cpu: 500m
        memory: 512Mi
 
# Shared Kafka for control plane events
kafka:
  enabled: false  # Using Strimzi in data plane

matih-data-plane Umbrella Chart

The data plane umbrella chart is larger, deploying all services that power analytics, AI, ML, and data processing.

Chart.yaml

apiVersion: v2
name: matih-data-plane
description: MATIH Data Plane umbrella chart - deploys all data plane services
type: application
version: 1.0.0
appVersion: "1.0.0"
 
home: https://github.com/matih/matih-prototype
icon: https://matih.ai/logo.png
 
keywords:
  - matih
  - data-platform
  - analytics
  - ai
  - ml
 
maintainers:
  - name: MATIH Engineering
    email: engineering@matih.ai
 
dependencies:
  - name: query-engine
    version: ">=0.1.0"
    repository: "file://../query-engine"
    condition: query-engine.enabled
 
  - name: catalog-service
    version: ">=0.1.0"
    repository: "file://../catalog-service"
    condition: catalog-service.enabled
 
  - name: pipeline-service
    version: ">=0.1.0"
    repository: "file://../pipeline-service"
    condition: pipeline-service.enabled
 
  - name: semantic-layer
    version: ">=0.1.0"
    repository: "file://../semantic-layer"
    condition: semantic-layer.enabled
 
  - name: bi-service
    version: ">=0.1.0"
    repository: "file://../bi-service"
    condition: bi-service.enabled
 
  - name: ai-service
    version: ">=0.1.0"
    repository: "file://../ai-service"
    condition: ai-service.enabled
 
  - name: data-plane-agent
    version: ">=0.1.0"
    repository: "file://../data-plane-agent"
    condition: data-plane-agent.enabled
 
  - name: ml-service
    version: ">=0.1.0"
    repository: "file://../ml-service"
    condition: ml-service.enabled
 
  - name: data-quality-service
    version: ">=0.1.0"
    repository: "file://../data-quality-service"
    condition: data-quality-service.enabled
 
  - name: render-service
    version: ">=0.1.0"
    repository: "file://../render-service"
    condition: render-service.enabled
 
  - name: ops-agent-service
    version: ">=0.1.0"
    repository: "file://../data-plane/ops-agent-service"
    condition: ops-agent-service.enabled
 
annotations:
  category: Data Platform
  licenses: Apache-2.0

Data Plane Service Matrix

ServiceTechnologyPortDefault ReplicasHPA Enabled
query-engineJava/Spring Boot80802Yes
catalog-serviceJava/Spring Boot80862Yes
pipeline-serviceJava/Spring Boot80922Yes
semantic-layerJava/Spring Boot80862Yes
bi-serviceJava/Spring Boot80842Yes
ai-servicePython/FastAPI80002Yes
data-plane-agentJava/Spring Boot80852No
ml-servicePython/FastAPI80002Yes
data-quality-servicePython/FastAPI80002Yes
render-serviceNode.js80982Yes
ops-agent-servicePython/FastAPI80801No

Helm Deep Merge Behavior

Understanding Helm's deep merge is critical when working with umbrella charts. Helm merges override files into the base values using these rules:

Merge Rules

Data TypeBehaviorExample
Scalar (string, int, bool)Override replaces basereplicaCount: 1 replaces replicaCount: 2
Map/ObjectKeys merge recursivelyBase securityContext.runAsUser: 1000 + override securityContext.readOnlyRootFilesystem: false = both keys present
Array/ListOverride replaces base entirelyOverride tolerations: [] replaces any base tolerations

The Deep Merge Trap

Consider this scenario:

# values.yaml (base)
securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  runAsGroup: 1000
  readOnlyRootFilesystem: true
  capabilities:
    drop:
      - ALL
 
# values-dev.yaml (override)
securityContext:
  runAsUser: 0  # Run as root for debugging

The merged result is:

# Merged result (UNEXPECTED)
securityContext:
  runAsNonRoot: true    # LEAKED from base - conflicts with runAsUser: 0!
  runAsUser: 0          # From override
  runAsGroup: 1000      # LEAKED from base
  readOnlyRootFilesystem: true  # LEAKED from base
  capabilities:
    drop:
      - ALL             # LEAKED from base

The runAsNonRoot: true leaked from the base and conflicts with runAsUser: 0, causing a container creation error.

The Correct Approach

Always override ALL keys in a section when any key changes:

# values-dev.yaml (CORRECT)
securityContext:
  runAsNonRoot: false
  runAsUser: 0
  runAsGroup: 0
  readOnlyRootFilesystem: false
  capabilities:
    drop: []

Deep Dive: The deep merge behavior is especially dangerous with umbrella charts because values flow through two levels: the umbrella chart merges its values with the subchart defaults. If the umbrella chart sets ai-service.securityContext.runAsUser: 0 but the subchart's values.yaml has securityContext.runAsGroup: 1000, the merged result will contain both -- potentially creating an invalid configuration.


Dependency Management

Building Dependencies

Before deploying an umbrella chart, dependencies must be resolved:

# Download and package all dependencies
helm dependency build infrastructure/helm/matih-control-plane
 
# Update dependencies (fetches latest matching versions)
helm dependency update infrastructure/helm/matih-data-plane

This creates .tgz archives in the charts/ directory for remote dependencies and symlinks for local file dependencies.

Dependency Conditions

Each dependency has a condition field that controls whether the subchart is deployed:

dependencies:
  - name: ai-service
    condition: ai-service.enabled  # Only deployed if ai-service.enabled = true

In the umbrella values:

# Enable all services
ai-service:
  enabled: true
 
# Disable a service during maintenance
pipeline-service:
  enabled: false

Selective Deployment

During development or troubleshooting, you can deploy a subset of services:

# values-minimal.yaml - Deploy only core services
ai-service:
  enabled: true
query-engine:
  enabled: true
catalog-service:
  enabled: true
semantic-layer:
  enabled: true
 
# Disable non-essential services
bi-service:
  enabled: false
ml-service:
  enabled: false
data-quality-service:
  enabled: false
render-service:
  enabled: false
ops-agent-service:
  enabled: false
pipeline-service:
  enabled: false

Deployment Workflow

Full Deployment

# Build dependencies
helm dependency build infrastructure/helm/matih-data-plane
 
# Deploy with environment overlay
helm upgrade --install matih-data-plane \
  infrastructure/helm/matih-data-plane \
  -f infrastructure/helm/matih-data-plane/values.yaml \
  --namespace matih-data-plane \
  --create-namespace \
  --timeout 10m \
  --wait

Rolling Updates via Image Tags

The CD pipeline updates services by setting image tags:

helm upgrade --install matih-data-plane \
  infrastructure/helm/matih-data-plane \
  -f infrastructure/helm/matih-data-plane/values.yaml \
  --namespace matih-data-plane \
  --set ai-service.image.tag=1.0.0-abc1234 \
  --set query-engine.image.tag=1.0.0-def5678 \
  --set catalog-service.image.tag=1.0.0-ghi9012 \
  --timeout 10m \
  --wait

Rollback

# View release history
helm history matih-data-plane --namespace matih-data-plane
 
# Rollback to previous revision
helm rollback matih-data-plane 3 --namespace matih-data-plane --timeout 5m

Subchart Value Propagation

Values in the umbrella chart propagate to subcharts using the subchart name as a key prefix:

# Umbrella values.yaml
 
# Global values (available to all subcharts via .Values.global)
global:
  environment: production
  imageRegistry: matihlabsacr.azurecr.io
 
# Subchart-specific values (passed to ai-service subchart)
ai-service:
  enabled: true
  replicaCount: 2
  image:
    registry: matihlabsacr.azurecr.io
    repository: matih/ai-service
    tag: "1.0.0"
  resources:
    requests:
      cpu: 500m
      memory: 1Gi
 
# Another subchart
query-engine:
  enabled: true
  replicaCount: 3
  image:
    registry: matihlabsacr.azurecr.io
    repository: matih/query-engine
    tag: "1.0.0"

Inside the ai-service subchart templates, these values are accessed normally:

# The subchart sees .Values.replicaCount = 2
# The subchart sees .Values.image.tag = "1.0.0"
# The subchart sees .Values.global.environment = "production"
replicas: {{ .Values.replicaCount }}

Umbrella Chart vs Individual Charts

MATIH supports both deployment modes:

AspectUmbrella ChartIndividual Charts
Deployment unitAll services in one releaseOne service per release
Rollback granularityAll services roll back togetherPer-service rollback
Deployment speedSlower (all services)Faster (single service)
Use caseInitial deployment, full upgradesHotfixes, single-service updates
CD pipeline stageservices stageservice-build-deploy.sh
Value managementCentralized in umbrellaPer-chart values

In practice, MATIH uses umbrella charts for initial deployment and coordinated releases, while individual charts are used for targeted service updates via scripts/tools/service-build-deploy.sh.


Troubleshooting

Common Umbrella Chart Issues

IssueCauseResolution
Error: chart requires kubeVersionSubchart version mismatchRun helm dependency update
Subchart not deployingenabled: false in valuesCheck <subchart>.enabled value
Values not reaching subchartWrong key nestingValues must be under <subchart-name>: key
cannot load Chart.lockStale lock fileDelete Chart.lock and run helm dependency build
Subchart resource conflictsDuplicate resource namesCheck fullnameOverride in subchart values
Old subchart still deployingStale .tgz in charts/Delete charts/ directory and rebuild

Next Steps