How to Merge Helm Chart Values Per Environment in Python

Merge default Helm chart values with environment-specific overrides using a recursive dictionary merge function, then write each environment's YAML file.

Easy Python 3.9+ Aug 9, 2026 Production deployment patterns 11 views 0 copies

Python code

41 lines
Python 3.9+
from pathlib import Path
import json
import tempfile


DEFAULT_VALUES = {
    "image": "nginx:latest",
    "replicas": 1,
    "resources": {"cpu": "100m", "memory": "128Mi"},
}

ENV_OVERRIDES = {
    "dev": {"replicas": 1, "resources": {"cpu": "50m"}},
    "staging": {"replicas": 2, "resources": {"cpu": "250m", "memory": "256Mi"}},
    "prod": {"replicas": 4, "resources": {"cpu": "500m", "memory": "1Gi"}, "image": "nginx:1.21.6"},
}


def merge_values(base: dict, override: dict) -> dict:
    result = base.copy()
    for key, value in override.items():
        if isinstance(value, dict) and isinstance(result.get(key), dict):
            result[key] = merge_values(result[key], value)
        else:
            result[key] = value
    return result


def generate_chart_values(env: str) -> dict:
    if env not in ENV_OVERRIDES:
        raise ValueError(f"Unknown environment: {env}")
    return merge_values(DEFAULT_VALUES, ENV_OVERRIDES[env])


if __name__ == "__main__":
    with tempfile.TemporaryDirectory() as tmpdir:
        for env in ["dev", "staging", "prod"]:
            values = generate_chart_values(env)
            out_path = Path(tmpdir) / f"values-{env}.yaml"
            out_path.write_text(json.dumps(values, indent=2))
            print(f"{env}: {out_path} -> {json.dumps(values)}")

Output

stdout
dev: /tmp/tmp123456/values-dev.yaml -> {"image": "nginx:latest", "replicas": 1, "resources": {"cpu": "50m", "memory": "128Mi"}}
staging: /tmp/tmp123456/values-staging.yaml -> {"image": "nginx:latest", "replicas": 2, "resources": {"cpu": "250m", "memory": "256Mi"}}
prod: /tmp/tmp123456/values-prod.yaml -> {"image": "nginx:1.21.6", "replicas": 4, "resources": {"cpu": "500m", "memory": "1Gi"}}

How it works

The merge_values function recursively merges two dictionaries: if both the base and override have a dict for the same key, it merges them recursively rather than replacing the entire dict. This allows partial overrides like updating only the CPU in the resources dict while keeping the memory value from the defaults. The generate_chart_values function validates the environment name and then applies the override to the defaults. The script writes each environment's merged values as a JSON file (a stand-in for YAML in this example) inside a temporary directory, which is useful for generating Helm values files during deployment.

Common mistakes

  • Using a shallow copy or .update() for nested dicts, which replaces the whole resources dict instead of merging nested keys.
  • Forgetting to validate the environment name, leading to silent default values instead of an error.
  • Writing YAML as plain text without proper quoting or formatting, which can break Helm parsing.
  • Hardcoding file paths instead of using tempfile or a configured output directory.

Variations

  1. Use PyYAML to actually write YAML files instead of JSON by adding yaml.safe_dump(values).
  2. Load overrides from separate YAML files per environment instead of a hardcoded dict.

Real-world use cases

  • CI/CD pipelines that generate per-environment Helm values files before deploying to dev, staging, and prod clusters.
  • Multi-tenant SaaS deployments where each customer or region requires different resource limits and image versions.
  • Infrastructure-as-code scripts that need to merge cloud-provider-specific settings with common service defaults.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Production deployment patterns

Related tutorials and quizzes for this topic.