Manage Django Kubernetes Manifests With Kustomize Overlays
Structure Django Kubernetes manifests with Kustomize: a base with Deployment, Service, and migration Job, overlays for staging and production with replicas, resources, and hosts, image tags set from CI, ConfigMap generators with hash suffixes, and use with Argo CD.
Problem statement
Your Django app has nearly identical Kubernetes manifests for staging and production, copied and edited by hand: different replica counts, host names, resource limits, and image tags. Copies drift, and a fix applied to one environment is forgotten in the other. Kustomize, built into kubectl, lets you keep one base and small per-environment patches, with no templating language.
- Put shared manifests in
base/: web Deployment, worker Deployment, Service, migration Job, and akustomization.yamllisting them. - Create
overlays/stagingandoverlays/production, each referencing../../baseand adding patches. - Generate ConfigMaps with
configMapGenerator; the hash suffix triggers rollouts when config changes. - Set the image tag per release with
kustomize edit set imagein CI, or let Argo CD do it. - Preview with
kubectl kustomize overlays/productionand apply withkubectl apply -k overlays/production.
Step-by-step solution
1. Lay out the repository
deploy/
base/
kustomization.yaml
web-deployment.yaml
worker-deployment.yaml
service.yaml
overlays/
staging/
kustomization.yaml
replicas.yaml
production/
kustomization.yaml
replicas.yaml
resources.yaml
hpa.yaml
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- web-deployment.yaml
- worker-deployment.yaml
- service.yaml
labels:
- pairs: { app.kubernetes.io/name: shop }
includeSelectors: true
images:
- name: shop
newName: ghcr.io/acme/shop
In the base manifests, refer to the image simply as shop; the images field rewrites it.
2. Write overlays
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: shop-prod
resources:
- ../../base
- hpa.yaml
patches:
- path: replicas.yaml
- path: resources.yaml
configMapGenerator:
- name: django-config
literals:
- DJANGO_SETTINGS_MODULE=config.settings.production
- ALLOWED_HOSTS=shop.example.com
images:
- name: shop
newTag: 3f2a1c9
# overlays/production/replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata: { name: web }
spec:
replicas: 3
3. Let config changes roll pods
configMapGenerator names the ConfigMap django-config-7g2kfh9m4b, with a hash of its content, and rewrites references in the Deployment. Changing a value changes the name, which changes the pod template, which triggers a rolling update. That fixes the common problem of edited ConfigMaps not reaching running pods. Use secretGenerator only for non-sensitive values or with encrypted files; real secrets belong in External Secrets or SOPS.
4. Migrations per release
A migration Job with a fixed name cannot be re-run, because Jobs are immutable. Options:
- Add
nameSuffixper release in CI, or generate the Job name with the image tag. - With Argo CD, use a
PreSynchook annotation on the Job so it runs before each sync and is deleted afterwards.
5. Set images from CI and apply
cd deploy/overlays/production
kustomize edit set image shop=ghcr.io/acme/shop:$GIT_SHA
git commit -am "Deploy $GIT_SHA" && git push # GitOps
# or directly:
kubectl apply -k deploy/overlays/production
Review the rendered output in pull requests with kubectl kustomize deploy/overlays/production | kubectl diff -f -.
Explanation
Kustomize reads a kustomization.yaml, loads resources, applies transformations (namespace, labels, image rewrites, name suffixes), and merges patches by matching kind and name. The output is plain Kubernetes YAML. Because there is no templating, manifests remain valid YAML you can read and lint, and overlays express differences explicitly. kubectl apply -k runs the same engine.
Edge cases / notes
- Selector labels. Changing labels with
includeSelectors: trueafter the first deploy alters immutable selectors; set them once. - Version skew. The
kustomizeCLI may be newer than kubectl's embedded version; pin one in CI. - Components. Kustomize components let optional features (for example a debug sidecar) be added to some overlays only.
Next steps
- Compare with Deploy Django With a Helm Chart.
- Automate syncs with Deploy Django With Argo CD GitOps.
- Run migrations properly with Run Django Migrations as a Kubernetes Job.
FAQ
Is Kustomize built into kubectl?
Yes. kubectl apply -k and kubectl kustomize use an embedded version.
How do I change the image tag per release?
Use kustomize edit set image in CI, or the images field in the overlay.
Why does changing a ConfigMap not restart pods?
Plain ConfigMaps do not change the pod template. Use configMapGenerator, which adds a content hash to the name.
Should secrets go in secretGenerator?
Not real secrets in plain files. Use External Secrets Operator or encrypted files with SOPS.