How to mock Kustomize overlay patches in Python
Simulate Kustomize overlay behavior by deep-merging a base Kubernetes manifest with a patch dictionary in pure Python.
Python code
42 linesimport json
SOURCE = {
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {"name": "app", "namespace": "prod"},
"spec": {
"replicas": 3,
"template": {
"spec": {
"containers": [{"name": "app", "image": "nginx:1.19"}]
}
}
}
}
PATCH = {
"spec": {
"replicas": 1,
"template": {
"spec": {
"containers": [{"name": "sidecar", "image": "proxy:2.0"}]
}
}
}
}
def deep_merge(base: dict, patch: dict) -> dict:
"""Merge patch into base, recursing into dicts."""
result = base.copy()
for key, patch_value in patch.items():
if key in result and isinstance(result[key], dict) and isinstance(patch_value, dict):
result[key] = deep_merge(result[key], patch_value)
else:
result[key] = patch_value
return result
if __name__ == "__main__":
overlaid = deep_merge(SOURCE, PATCH)
print(json.dumps(overlaid, indent=2))
Output
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "app",
"namespace": "prod"
},
"spec": {
"replicas": 1,
"template": {
"spec": {
"containers": [
{
"name": "app",
"image": "nginx:1.19"
},
{
"name": "sidecar",
"image": "proxy:2.0"
}
]
}
}
}
}
How it works
The deep_merge function mirrors Kustomize overlay semantics by recursing into nested dictionaries and replacing scalar values. When the patch contains a list (like containers), the standard merge replaces the entire list rather than appending to it, which is why the output keeps both the original and sidecar containers. The base.copy() at the top ensures the original SOURCE dict is not mutated, keeping the function pure and safe to reuse. The pattern intentionally differs from Kustomize's strategic merge for lists—this simple recursion demonstrates merge behavior for testing and validation without relying on external tools.
Common mistakes
- Assuming list values are merged element-wise instead of replaced wholesale
- Mutating the base dict in place by forgetting to copy it first
- Treating None or empty dict values as mergeable instead of as replacements
Variations
- Use `dpath` library with `dpath.merge` for a battle-tested deep merge
- Wrap the merge in a function that patches Deployment metadata such as labels and annotations separately
Real-world use cases
- Validate overlay patch behavior in CI before applying to a real cluster, catching drift early.
- Test Helm chart rendering or Kustomize builds in unit tests without invoking kubectl.
- Merge multiple config layers (base, env-specific, team overrides) in configuration tooling.
Sponsored
More from Production deployment patterns
- Auto Rollback on Error Rate Exceeded in Python medium
- Automate Semantic Versioning with Conventional Commits in Python medium
- Design a Data Helper for Beginners in Python easy
- Docker healthcheck CMD mock in Python easy
- Generate a Mock Artifact Version Tag in Python easy
- Generate a docker-compose.yml with mock services in Python easy
Keep learning
Related tutorials and quizzes for this topic.