Skip to content

Environment Strategy

How to handle dev, staging, and production with the same chart using Helm value overlay files.


The Pattern

One chart, multiple value files:

charts/azure-base/
├── values.yaml           # Defaults (dev-friendly, cheap)
├── values-dev.yaml       # Explicit dev overrides
├── values-staging.yaml   # Staging overrides
└── values-prod.yaml      # Production (GRS, premium KV, purge protection)

Deploy by selecting the right overlay:

bash
helm install azure-base-dev     ./charts/azure-base -f ./charts/azure-base/values-dev.yaml
helm install azure-base-staging ./charts/azure-base -f ./charts/azure-base/values-staging.yaml
helm install azure-base-prod    ./charts/azure-base -f ./charts/azure-base/values-prod.yaml

TIP

Each install creates a separate Helm release with a unique name. This means you can run all three environments in the same cluster (useful for dev/testing) or in separate clusters (typical for staging/prod).


What Changes Between Environments

Network Isolation

Each environment uses a separate address space to prevent overlap:

Full Comparison

SettingDevStagingProd
Locationeastuseastuswesteurope
VNet CIDR10.10.0.0/1610.20.0.0/1610.30.0.0/16
Storage ReplicationLRS (local)ZRS (zone)GRS (geo)
Key Vault SKUstandardstandardpremium
Purge Protectionoffoffon
Soft Delete Days73090
Blob Containersdatadata, logsdata, logs, backups
Public KV Accessyesyesno
KV Deployment Accessnonoyes
KV Disk Encryptionnonoyes
NSG RulesHTTPS onlyHTTPS + HTTPHTTPS only
Subnet Service EndpointsStorage, KeyVaultStorage, KeyVaultStorage, KeyVault, Sql

Why These Specific Differences

Storage replication:

  • Dev uses LRS (3 copies in one datacenter) — cheapest, sufficient for throwaway data
  • Staging uses ZRS (3 copies across zones) — tests zone-redundancy behavior
  • Prod uses GRS (6 copies across regions) — survives full region failure

Key Vault:

  • Dev/staging use standard — no HSM-backed keys needed
  • Prod uses premium — supports HSM-backed keys for compliance
  • Prod enables purge protection — prevents accidental permanent deletion (cannot be undone)
  • Prod disables public access — only VNet-integrated services can reach it

Containers:

  • Dev only needs data — minimal resource usage
  • Staging adds logs — tests log pipeline integration
  • Prod adds backups — backup retention storage

How Helm Value Overlays Work

values.yaml contains the defaults. Environment files override specific values.

Helm deep-merges the files. Only values present in the overlay file are overridden; everything else keeps the default from values.yaml.

Example — values.yaml has:

yaml
storage:
  accountTier: Standard
  accountReplicationType: LRS
  accountKind: StorageV2
  httpsOnly: true
  minTlsVersion: TLS1_2
  containers:
    - name: data
      accessType: private
    - name: logs
      accessType: private

values-prod.yaml overrides:

yaml
storage:
  accountReplicationType: GRS
  containers:
    - name: data
      accessType: private
    - name: logs
      accessType: private
    - name: backups
      accessType: private

Result: accountTier stays Standard (from defaults), accountReplicationType becomes GRS (from prod), and containers are fully replaced (YAML arrays replace, they don't merge).

Array Replacement

Helm replaces arrays entirely, it does not merge them. If values.yaml has 2 containers and values-prod.yaml specifies 3 containers, you get exactly those 3 — not 5. This is why every environment file specifies the full container list.


Naming Strategy

The naming helper produces: {project}-{environment}-{suffix}

EnvironmentExample Resource GroupExample VNet
devmyapp-dev-rgmyapp-dev-vnet
stagingmyapp-staging-rgmyapp-staging-vnet
prodmyapp-prod-rgmyapp-prod-vnet

This means:

  • Resources never collide between environments
  • kubectl get managed shows which environment each resource belongs to
  • Azure portal filtering by tag environment=prod shows only prod resources

Multi-Cluster vs Single-Cluster

Single cluster (dev/testing)

bash
# All three environments coexist in one cluster
helm install azure-base-dev     ./charts/azure-base -f values-dev.yaml
helm install azure-base-staging ./charts/azure-base -f values-staging.yaml
helm install azure-base-prod    ./charts/azure-base -f values-prod.yaml

Works because:

  • Each Helm release has a unique name
  • Each resource has a unique metadata.name (different environment prefix)
  • Resources reference each other by name within the same environment

Multi-cluster (staging/prod)

bash
# Staging cluster
kubectl config use-context staging-cluster
helm install azure-base ./charts/azure-base -f values-staging.yaml

# Prod cluster
kubectl config use-context prod-cluster
helm install azure-base ./charts/azure-base -f values-prod.yaml

In multi-cluster, you can use the same Helm release name (azure-base) since there's no collision across clusters.


CI/CD Integration

Validation (no cluster needed)

bash
# Render and validate YAML for each environment
for env in dev staging prod; do
  echo "=== Validating $env ==="
  helm template azure-base-$env ./charts/azure-base \
    -f ./charts/azure-base/values-$env.yaml > /dev/null
  echo "$env: OK"
done

Deployment

bash
# Deploy to target environment
ENVIRONMENT=${ENVIRONMENT:-dev}

helm upgrade --install azure-base-$ENVIRONMENT ./charts/azure-base \
  -f ./charts/azure-base/values-$ENVIRONMENT.yaml \
  --wait --timeout 10m

helm upgrade --install is idempotent — it installs on first run and upgrades on subsequent runs.

Drift check

bash
# Compare desired state (Helm) with actual state (cluster)
helm get manifest azure-base-dev | kubectl diff -f -

If there's no diff, the cluster state matches the Helm release. If Crossplane has auto-corrected drift in Azure, the Kubernetes MR state will still match (Crossplane updates the MR status, not the spec).


Adding a New Environment

  1. Copy an existing values file:
bash
cp charts/azure-base/values-dev.yaml charts/azure-base/values-sandbox.yaml
  1. Edit the new file — change at minimum:

    • environment: sandbox
    • VNet address space (avoid overlap)
    • Any environment-specific settings
  2. Deploy:

bash
helm install azure-base-sandbox ./charts/azure-base \
  -f ./charts/azure-base/values-sandbox.yaml

No template changes needed. The naming helper and loops handle everything.

Released under the MIT License.