Run Django on Kubernetes

Khimananda Oli 8 min read Programming and Languages
Run Django on Kubernetes

By Khimananda Oli | Last reviewed: August 2026

You want to run Django on Kubernetes because your application has outgrown single-server deployments or you need the resilience and scaling that only an orchestrator provides. Moving from a traditional VPS or PaaS to K8s introduces complexity around statelessness, static assets, and process management that standard tutorials often gloss over. This guide bridges that gap by focusing on the specific architectural patterns required to make Django production-ready in a containerized environment, drawing on real-world deployments I have managed across AWS EKS and on-premise clusters.

Ingress ControllerDjango PodGunicorn + AppStatic SidecarPostgreSQL / RDSRedis CacheFigure 1: High-level architecture to run Django on Kubernetes securely
Core components required when you run Django on Kubernetes in production

How do you prepare a Django Dockerfile for Kubernetes?

The foundation of any attempt to containerize web applications correctly is the image itself. For Django, a naive Dockerfile leads to bloated images, security vulnerabilities, and slow deployments. You need a multi-stage build that separates dependencies from runtime artifacts.

Multi-stage build strategy

In practice, I always separate the builder stage from the runtime stage. This keeps your final image under 200MB and reduces the attack surface by excluding compilers and development headers. The runtime user should never be root.

# Builder stage
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# Runtime stage
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PATH="/root/.local/bin:$PATH"
    
RUN addgroup --system django && \
    adduser --system --ingroup django django

WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
RUN chown -R django:django /app

USER django
EXPOSE 8000
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000"]

This pattern ensures that system packages like gcc or libpq-dev used during compilation do not persist in the deployed pod. When you run Django on Kubernetes, smaller images mean faster node scaling and reduced network egress costs.

Why Gunicorn is mandatory

Never run manage.py runserver in a cluster. It is single-threaded, insecure, and lacks process management. Gunicorn (or Uvicorn for async Django) handles concurrent requests and worker recycling. Configure workers based on CPU cores available to the pod, typically (2 x CPU_CORES) + 1. If you are unsure about resource sizing, review Kubernetes resource limits and requests before setting worker counts.

How do you manage Django settings and secrets in Kubernetes?

Hardcoding credentials in Dockerfiles or environment variables defined directly in Deployment specs is a security failure. When operating in regulated environments, treating configuration as code is non-negotiable. Kubernetes offers native primitives for this separation.

ConfigMaps vs Secrets

Use ConfigMaps for non-sensitive data like DJANGO_ALLOWED_HOSTS, log levels, or feature flags. Use Secrets for database passwords, API keys, and the DJANGO_SECRET_KEY. While K8s Secrets are base64-encoded by default, they are not encrypted at rest unless you configure the cluster's encryption provider. For higher security standards, integrate external secret stores.

  • ConfigMap: Store settings.yaml or individual env vars for environment-specific overrides.
  • Secret: Store DB_PASSWORD and SECRET_KEY. Mount as files or env vars.
  • External Secrets Operator: Sync from AWS Secrets Manager, Vault, or Azure Key Vault automatically.

If you are managing sensitive data, follow the patterns in Kubernetes secrets management done right to avoid common pitfalls like committing encoded secrets to Git.

Injecting configuration safely

I prefer mounting secrets as files rather than environment variables where possible, as env vars can leak in crash logs or child processes. However, Django’s os.environ pattern is ubiquitous. If using env vars, ensure your pod security context prevents unauthorized access to /proc/self/environ.

env:
  - name: DJANGO_SECRET_KEY
    valueFrom:
      secretKeyRef:
        name: django-secrets
        key: secret-key
  - name: DATABASE_URL
    valueFrom:
      secretKeyRef:
        name: django-db-creds
        key: url

How do you handle static files when running Django on Kubernetes?

This is the most frequent point of failure for teams migrating Django to K8s. Containers are ephemeral; if Pod A collects static files into its local filesystem, Pod B will not see them. Load balancers will route users randomly, resulting in broken CSS/JS half the time.

Option A: Object StorageS3/GCS + WhiteNoiseRecommended for ScaleOption B: Shared PVCReadWriteMany VolumeComplex Stateful SetupOption C: Nginx SidecarInit Container CopySelf-Contained PodsDecision Factor: Team Size, Cloud Budget, and Compliance RequirementsFigure 2: Static file handling options when you run Django on Kubernetes
Choosing the right static file strategy for your Django Kubernetes deployment

For most production systems, offloading static and media files to S3, Google Cloud Storage, or MinIO is the correct architectural choice. Use django-storages and WhiteNoise. This makes your Django pods truly stateless and horizontally scalable without shared volume bottlenecks.

Nginx sidecar pattern

If you cannot use external object storage due to compliance or air-gapped constraints, use an init container to collect static files into an emptyDir volume shared with an Nginx sidecar. The Nginx container serves /static/ directly, bypassing Gunicorn entirely. This adds pod overhead but removes external dependencies.

Shared Persistent Volumes

Avoid ReadWriteMany NFS volumes for static files unless absolutely necessary. They introduce latency, single points of failure, and permission headaches. If you must use persistent storage for media uploads, consider Longhorn distributed storage or cloud-native block storage with proper backup policies.

How do you configure readiness probes for Django?

Kubernetes needs to know when your Django app is actually ready to accept traffic. A common mistake is using a simple TCP check on port 8000. The port may be open while the application is still loading models or waiting for database migrations. This causes 502 errors during rolling updates.

Implementing a health endpoint

Create a dedicated view that verifies critical dependencies. Do not put heavy logic here; it runs every few seconds. Return 200 only if the DB connection works and essential caches are reachable.

# views.py
from django.http import JsonResponse
from django.db import connection

def health_check(request):
    try:
        with connection.cursor() as cursor:
            cursor.execute("SELECT 1")
        return JsonResponse({"status": "ok"}, status=200)
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=503)

Probe configuration

Set initialDelaySeconds generously for Django apps. Cold starts with many installed apps can take 5–10 seconds. Premature liveness checks will restart your pod in a CrashLoopBackOff. If debugging startup issues, refer to debugging CrashLoopBackOff in Kubernetes.

Probe TypePurposeDjango Configuration Tip
LivenessRestart stuck podsCheck Gunicorn worker responsiveness, not just DB
ReadinessGate traffic during startupVerify DB + Cache connectivity; fail fast if missing
StartupAllow slow initializationSet failureThreshold × periodSeconds > max boot time

How do you automate Django deployments with GitOps?

Manually applying YAML files with kubectl does not scale and creates audit gaps. In 2026, GitOps is the standard for running Django on Kubernetes reliably. Your desired state lives in Git; a reconciler ensures the cluster matches it.

ArgoCD or Flux

Tools like ArgoCD watch your repository and sync changes automatically. This provides drift detection, rollback capability, and visual deployment tracking. For teams managing multiple environments (staging, production), parameterize manifests using Helm or Kustomize. See setting up GitOps with ArgoCD for implementation details.

Database migration strategy

Migrations are the riskiest part of a Django deployment. Never run them inside the main application container’s entrypoint. If multiple pods start simultaneously, race conditions corrupt your schema. Instead, use a Kubernetes Job that runs before the new deployment rolls out. In ArgoCD, use PreSync hooks; in Helm, use pre-install/pre-upgrade hooks.

# migration-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: django-migrate
  annotations:
    argocd.argoproj.io/hook: PreSync
spec:
  template:
    spec:
      containers:
      - name: migrate
        image: myregistry/django-app:v2.1.0
        command: ["python", "manage.py", "migrate", "--noinput"]
        envFrom:
        - secretRef:
            name: django-db-creds
      restartPolicy: Never
  backoffLimit: 1
Git PushArgoCD SyncPreSync: Migrate JobDeploy New PodsHealth Check PassFigure 3: Safe migration and deployment flow when you run Django on Kubernetes
GitOps workflow ensuring safe migrations before updating Django pods

Run Django on Kubernetes with Confidence

Successfully operating Django in a cluster requires respecting the framework’s characteristics while adapting to cloud-native constraints. Focus on immutable images, externalized configuration, stateless static file serving, and atomic migrations. These patterns transform Kubernetes from a source of frustration into a reliable platform for growth. Whether you are deploying for a Kathmandu-based startup serving local users or a global SaaS platform, these fundamentals remain constant.

If your team needs help architecting a compliant, observable Django infrastructure or auditing an existing deployment, reach out to discuss your project. Getting the foundation right now prevents costly rework and downtime later.

Frequently Asked Questions

Use Helm charts with Gunicorn and Uvicorn workers behind an NGINX Ingress. Configure liveness probes checking /healthz/ endpoints. Store secrets in External Secrets Operator and use PersistentVolumeClaims for media files. This pattern ensures production-grade reliability for running Django on Kubernetes clusters efficiently.

Run collectstatic during container build or via init containers. Serve assets through CloudFront or GCS buckets using django-storages. Never serve static content from application pods in production. This decouples asset delivery from compute, reducing pod resource usage when you run Django on Kubernetes at scale.

No. SQLite requires local filesystem writes which fail across ephemeral pods. Use PostgreSQL or MySQL with managed cloud databases instead. Shared storage introduces latency and corruption risks. Always externalize stateful data stores when you run Django on Kubernetes to maintain consistency and high availability.

Use Sealed Secrets or External Secrets Operator to sync from AWS Secrets Manager or Vault. Never commit credentials to Git or embed them in Dockerfiles. Inject secrets as environment variables or mounted volumes at runtime. This prevents exposure while maintaining secure configuration management when you run Django on Kubernetes.

Start with 256Mi memory requests and 512Mi limits per Gunicorn worker. Set CPU requests to 250m with 1000m limits. Monitor actual usage via Prometheus metrics for two weeks before adjusting. Over-provisioning wastes money; under-provisioning causes OOMKills when you run Django on Kubernetes under load.

Execute migrations as a Kubernetes Job before deployment, not during pod startup. Use helm pre-install hooks or Argo CD sync waves to sequence database changes. Roll back failed jobs automatically. This prevents partial schema updates and race conditions when multiple pods start simultaneously during Django on Kubernetes deployments.

Only above ten instances monthly. Below that threshold, Render or Railway cost less with zero ops overhead. Kubernetes savings emerge at scale through spot instances and bin packing. Calculate total cost including engineer time before choosing to run Django on Kubernetes for budget-constrained projects in 2026.

Create a dedicated /healthz/ view returning 200 OK without database queries. Use /readyz/ for readiness probes that verify DB connectivity. Set initialDelaySeconds to thirty and periodSeconds to ten. Proper probe configuration prevents traffic routing to unprepared pods when you run Django on Kubernetes.

Yes. Deploy Celery workers as separate Deployments with autoscaling based on queue length. Use Redis or RabbitMQ as broker with dedicated StatefulSets. Configure flower monitoring as independent service. Isolating async tasks from web pods improves fault tolerance when you run Django on Kubernetes architectures.

Terminate TLS at ingress controller level using cert-manager for automatic Let's Encrypt certificates. Configure Django SECURE_PROXY_SSL_HEADER to trust X-Forwarded-Proto. Never terminate SSL inside application containers. This simplifies certificate rotation and reduces CPU overhead when you run Django on Kubernetes with public-facing services.

Output structured JSON logs to stdout only. Use Fluent Bit DaemonSet to forward logs to Elasticsearch or Loki. Never write logs to pod filesystems since they disappear on restart. Centralized aggregation enables correlation across distributed components when you run Django on Kubernetes observability stacks.

Check network policies blocking egress to databases. Verify DNS resolution with nslookup inside pods. Inspect service endpoint slices for missing targets. Review Gunicorn worker timeout settings against slow query durations. These diagnostics resolve most networking issues when troubleshooting Django on Kubernetes connectivity failures.

Yes for event-driven workloads. Scale web pods based on pending HTTP requests in ingress queue rather than CPU alone. Configure minimum replicas to prevent cold starts. KEDA provides finer-grained scaling than HPA for bursty traffic patterns when you run Django on Kubernetes serving variable loads.

Store uploads directly in S3 or Azure Blob Storage using signed URLs. Bypass pod filesystem entirely to avoid PVC bottlenecks. Configure CORS policies for browser-based direct uploads. This eliminates shared storage complexity and scales independently when you run Django on Kubernetes handling user-generated content.

Skipping read-only root filesystem enforcement, ignoring pod disruption budgets, and misconfiguring timezone environment variables cause frequent outages. Always validate manifests with kubeval before applying. Test failure scenarios in staging first. These oversights create silent failures that surface only after deploying Django on Kubernetes to production environments.