Kustomize Integration
Kustomize lets you manage variations of OpenMCF manifests without duplication. Instead of maintaining separate manifest files for dev, staging, and production, you maintain one base manifest and environment-specific overlays that patch the base.
For the conceptual overview of manifest sources (including Kustomize), see Manifests. For flag details, see CLI Reference.
manifests/database/
|-- base/
| \-- database.yaml # Shared configuration
\-- overlays/
|-- dev/
|-- staging/
\-- prod/ # Environment-specific patches
OpenMCF integrates Kustomize as a Go library (sigs.k8s.io/kustomize), not as an external binary. The --kustomize-dir and --overlay flags trigger Kustomize to build the final manifest at deployment time.
Quick Start
1. Create Base Manifest
mkdir -p services/api/kustomize/base
services/api/kustomize/base/deployment.yaml:
apiVersion: kubernetes.openmcf.org/v1
kind: KubernetesDeployment
metadata:
name: api
spec:
container:
image:
repo: myapp/api
tag: latest
replicas: 1
resources:
limits:
cpu: 500m
memory: 512Mi
services/api/kustomize/base/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
2. Create Environment Overlay
services/api/kustomize/overlays/prod/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: patch.yaml
services/api/kustomize/overlays/prod/patch.yaml:
apiVersion: kubernetes.openmcf.org/v1
kind: KubernetesDeployment
metadata:
name: api
spec:
container:
image:
tag: v1.0.0
replicas: 3
resources:
limits:
cpu: 2000m
memory: 4Gi
3. Deploy with OpenMCF
# Deploy to production
openmcf pulumi up \
--kustomize-dir services/api/kustomize \
--overlay prod
# Deploy to dev
openmcf pulumi up \
--kustomize-dir services/api/kustomize \
--overlay dev
What happens:
- OpenMCF runs
kustomize build services/api/kustomize/overlays/prod - Merges base + prod overlay into final manifest
- Validates the result
- Deploys using Pulumi or OpenTofu
Directory Structure
Standard Layout
<service-name>/kustomize/
|-- base/
| |-- kustomization.yaml # Base kustomization config
| \-- <resource>.yaml # Base resource definition
\-- overlays/
|-- dev/
| |-- kustomization.yaml # Dev environment config
| \-- patch.yaml # Dev-specific patches
|-- staging/
| |-- kustomization.yaml
| \-- patch.yaml
\-- prod/
|-- kustomization.yaml
\-- patch.yaml
Example: Complete Service
backend/services/api/kustomize/
|-- base/
| |-- kustomization.yaml
| |-- deployment.yaml
| \-- database.yaml
\-- overlays/
|-- dev/
| |-- kustomization.yaml
| |-- deployment-patch.yaml
| \-- database-patch.yaml
|-- staging/
| |-- kustomization.yaml
| |-- deployment-patch.yaml
| \-- database-patch.yaml
\-- prod/
|-- kustomization.yaml
|-- deployment-patch.yaml
\-- database-patch.yaml
Creating Patches
Strategic Merge Patches
The most common approach - specify only the fields you want to change:
Base:
apiVersion: kubernetes.openmcf.org/v1
kind: KubernetesPostgres
metadata:
name: app-database
spec:
container:
replicas: 1
resources:
limits:
cpu: 500m
memory: 1Gi
diskSize: 10Gi
Prod Patch (overlays/prod/patch.yaml):
apiVersion: kubernetes.openmcf.org/v1
kind: KubernetesPostgres
metadata:
name: app-database
spec:
container:
replicas: 3 # Override
resources:
limits:
cpu: 2000m # Override
memory: 4Gi # Override
diskSize: 100Gi # Override
Result: Base + patch merged = 3 replicas, 2000m CPU, 4Gi memory, 100Gi disk.
JSON 6902 Patches
For more complex changes:
overlays/prod/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- target:
kind: KubernetesPostgres
name: app-database
patch: |-
- op: replace
path: /spec/container/replicas
value: 3
- op: add
path: /metadata/labels/environment
value: production
Common Patterns
Pattern 1: Environment-Specific Resources
Different instance sizes per environment:
Dev (small, cheap):
spec:
container:
replicas: 1
resources:
limits:
cpu: 500m
memory: 512Mi
Prod (large, resilient):
spec:
container:
replicas: 5
resources:
limits:
cpu: 2000m
memory: 4Gi
Pattern 2: Environment-Specific Labels
Add labels for cost tracking:
overlays/prod/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
commonLabels:
environment: production
cost-center: engineering
team: backend
Pattern 3: Environment-Specific Images
overlays/dev/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
images:
- name: myapp/api
newTag: latest # Dev uses latest
patches:
- path: dev-patch.yaml
overlays/prod/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
images:
- name: myapp/api
newTag: v1.2.3 # Prod uses specific version
patches:
- path: prod-patch.yaml
Pattern 4: Shared Configuration + Environment Patches
base/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:
app: api
managed-by: openmcf
resources:
- deployment.yaml
- database.yaml
- cache.yaml
Each resource in base defines shared configuration, overlays patch for environment needs.
Workflow Examples
Deploying to Multiple Environments
# Deploy to dev
openmcf pulumi up \
--kustomize-dir services/api/kustomize \
--overlay dev \
--yes
# Test in dev...
# Deploy to staging
openmcf pulumi up \
--kustomize-dir services/api/kustomize \
--overlay staging \
--yes
# Test in staging...
# Deploy to production (with review)
openmcf pulumi preview \
--kustomize-dir services/api/kustomize \
--overlay prod
# Review changes...
openmcf pulumi up \
--kustomize-dir services/api/kustomize \
--overlay prod
Combining Kustomize with --set Overrides
# Kustomize overlay + runtime override
openmcf pulumi up \
--kustomize-dir services/api/kustomize \
--overlay prod \
--set spec.container.image.tag=v1.2.4
Order of precedence:
- Base manifest
- Overlay patches applied
--setoverrides applied last (highest priority)
Preview Built Manifest
# See what Kustomize generates (useful for debugging)
cd services/api/kustomize
kustomize build overlays/prod
# Or let OpenMCF build and show it
openmcf load-manifest \
--kustomize-dir services/api/kustomize \
--overlay prod
For using Kustomize in CI/CD pipelines with branch-based overlay selection, see CI/CD Integration.
Advanced Techniques
Multiple Bases
Useful for shared components:
common/
\-- base/
|-- kustomization.yaml
\-- shared-config.yaml
service-a/kustomize/
\-- overlays/
\-- prod/
|-- kustomization.yaml # References ../../../common/base
\-- patch.yaml
service-b/kustomize/
\-- overlays/
\-- prod/
|-- kustomization.yaml # Also references ../../../common/base
\-- patch.yaml
Components (Reusable Pieces)
components/monitoring/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
patches:
- path: add-monitoring.yaml
Use in overlay:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
components:
- ../../../components/monitoring
Generating ConfigMaps from Files
overlays/prod/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
configMapGenerator:
- name: app-config
files:
- config/prod.yaml
- config/secrets.encrypted
Troubleshooting
Error: "no such file or directory"
Problem: Kustomize can't find referenced files.
Solution:
# Check file paths in kustomization.yaml
# Ensure paths are relative to kustomization.yaml location
# Verify structure
ls -R services/api/kustomize/
Error: "kustomization.yaml not found"
Problem: Missing kustomization.yaml in overlay.
Solution:
# Create kustomization.yaml
cat > services/api/kustomize/overlays/prod/kustomization.yaml <<EOF
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
EOF
Patch Not Applied
Problem: Your patch isn't affecting the final output.
Solution:
# 1. Verify patch is listed in kustomization.yaml
cat overlays/prod/kustomization.yaml
# 2. Check patch targets correct resource
# - apiVersion must match
# - kind must match
# - metadata.name must match
# 3. Test kustomize build directly
cd services/api/kustomize
kustomize build overlays/prod
Wrong Overlay Applied
Problem: Deployed dev config to prod (or vice versa).
Solution:
# Always verify overlay before deploying
openmcf pulumi preview \
--kustomize-dir services/api/kustomize \
--overlay prod # Double-check this!
# Use explicit confirmation in CI/CD
if [ "$OVERLAY" != "prod" ]; then
echo "Deploying to $OVERLAY"
openmcf pulumi up --kustomize-dir ... --overlay $OVERLAY --yes
else
echo "Production deployment - manual approval required"
openmcf pulumi up --kustomize-dir ... --overlay $OVERLAY
fi
Best Practices
1. Keep Base Minimal
# ✅ Good: Base has common configuration
base/deployment.yaml:
name: api
container:
image:
repo: myapp/api
# No environment-specific values
# ❌ Bad: Base has production values
base/deployment.yaml:
name: api
container:
replicas: 10 # Production-specific, doesn't belong in base
2. One Overlay Per Environment
# Good
overlays/
|-- dev/
|-- staging/
\-- prod/
# Confusing
overlays/
|-- dev-us-west/
|-- dev-eu-central/
|-- staging-us-west/
\-- ... (too many combinations)
3. Use Descriptive Patch Names
# Good
overlays/prod/
|-- kustomization.yaml
|-- resources-patch.yaml # Increases resources
|-- replicas-patch.yaml # Scales replicas
\-- monitoring-patch.yaml # Adds monitoring
# Bad
overlays/prod/
|-- kustomization.yaml
|-- patch1.yaml
|-- patch2.yaml
\-- patch3.yaml
4. Version Control Everything
# ✅ Good: All kustomize files in Git
git add services/api/kustomize/
git commit -m "feat: add production overlay for API"
# ❌ Bad: Generated files, temp files committed
git add services/api/kustomize/overlays/prod/output.yaml # Generated
5. Test Overlays Before Deploying
# ✅ Good: Preview before applying
kustomize build overlays/prod | less # Review output
openmcf pulumi preview --kustomize-dir ... --overlay prod
# ⚠️ Risky: Deploy without review
openmcf pulumi up --kustomize-dir ... --overlay prod --yes
Complete Example
Here's a complete real-world example:
Directory Structure
backend/services/api/kustomize/
|-- base/
| |-- kustomization.yaml
| |-- deployment.yaml
| |-- database.yaml
| \-- redis.yaml
\-- overlays/
|-- dev/
| |-- kustomization.yaml
| |-- deployment-patch.yaml
| |-- database-patch.yaml
| \-- redis-patch.yaml
\-- prod/
|-- kustomization.yaml
|-- deployment-patch.yaml
|-- database-patch.yaml
\-- redis-patch.yaml
Files
base/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:
app: api
managed-by: openmcf
resources:
- deployment.yaml
- database.yaml
- redis.yaml
base/deployment.yaml:
apiVersion: kubernetes.openmcf.org/v1
kind: KubernetesDeployment
metadata:
name: api
spec:
container:
image:
repo: mycompany/api
tag: latest
replicas: 1
resources:
limits:
cpu: 500m
memory: 512Mi
overlays/prod/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
commonLabels:
environment: production
images:
- name: mycompany/api
newTag: v1.0.0
patches:
- path: deployment-patch.yaml
- path: database-patch.yaml
- path: redis-patch.yaml
overlays/prod/deployment-patch.yaml:
apiVersion: kubernetes.openmcf.org/v1
kind: KubernetesDeployment
metadata:
name: api
spec:
container:
replicas: 5
resources:
limits:
cpu: 2000m
memory: 4Gi
Deployment
# Deploy to production
openmcf pulumi up \
--kustomize-dir backend/services/api/kustomize \
--overlay prod
What's Next
- Writing Manifests — Manifest structure and best practices
- CI/CD Integration — Kustomize with GitHub Actions and GitLab CI
- Advanced Usage — Combining Kustomize with
--setoverrides - State Backends — Per-environment state configuration
- Official Kustomize Docs — Kustomize reference documentation
Next article