Deploy Django on Google Kubernetes Engine (GKE)
Deploy Django on GKE Autopilot: push images to Artifact Registry, connect to Cloud SQL with the Auth Proxy sidecar and Workload Identity, expose HTTPS with a Gateway or Ingress and managed certificates, and run migrations as a Job.
Problem statement
Google Kubernetes Engine runs Kubernetes with Google managing the control plane, and in Autopilot mode the nodes too. For a Django app that has outgrown a single server or Cloud Run's request model (Celery workers, WebSockets, many services), GKE is a natural step on Google Cloud. The Kubernetes basics are the same everywhere; the GKE-specific pieces are Artifact Registry for images, Cloud SQL with the Auth Proxy, Workload Identity for credentials, and Google-managed load balancers and certificates.
- Create a GKE Autopilot cluster and an Artifact Registry repository; push the Django image.
- Create a Cloud SQL for PostgreSQL instance and a Kubernetes service account bound to a Google service account via Workload Identity with
roles/cloudsql.client. - Run the Cloud SQL Auth Proxy as a native sidecar container; Django connects to
127.0.0.1:5432. - Store
SECRET_KEYand database credentials in Secret Manager or Kubernetes Secrets. - Expose the app with a Gateway (or Ingress) and a Google-managed certificate; set readiness probes and a BackendConfig or HealthCheckPolicy path.
- Run
migrateas a Kubernetes Job before rolling out the Deployment.
Step-by-step solution
1. Cluster and registry
gcloud container clusters create-auto django-prod --region europe-west1
gcloud artifacts repositories create apps --repository-format=docker --location=europe-west1
docker build -t europe-west1-docker.pkg.dev/$PROJECT/apps/myapp:$SHA .
docker push europe-west1-docker.pkg.dev/$PROJECT/apps/myapp:$SHA
Autopilot bills per pod resource request, so set realistic requests on every container.
2. Workload Identity for Cloud SQL and other APIs
gcloud iam service-accounts create django-app
gcloud projects add-iam-policy-binding $PROJECT --member "serviceAccount:django-app@$PROJECT.iam.gserviceaccount.com" --role roles/cloudsql.client
kubectl create serviceaccount django -n app
gcloud iam service-accounts add-iam-policy-binding django-app@$PROJECT.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser --member "serviceAccount:$PROJECT.svc.id.goog[app/django]"
kubectl annotate serviceaccount django -n app iam.gke.io/gcp-service-account=django-app@$PROJECT.iam.gserviceaccount.com
Pods using the django service account now get Google credentials automatically, for Cloud SQL, Cloud Storage (django-storages GCS backend), and Secret Manager, with no JSON key files.
3. Deployment with the Auth Proxy sidecar
apiVersion: apps/v1
kind: Deployment
metadata: { name: web, namespace: app }
spec:
replicas: 2
selector: { matchLabels: { app: web } }
template:
metadata: { labels: { app: web } }
spec:
serviceAccountName: django
initContainers:
- name: cloud-sql-proxy
image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.14.0
restartPolicy: Always # native sidecar
args: ["--structured-logs", "--port=5432", "PROJECT:REGION:INSTANCE"]
resources: { requests: { cpu: 100m, memory: 128Mi } }
containers:
- name: web
image: europe-west1-docker.pkg.dev/PROJECT/apps/myapp:SHA
ports: [{ containerPort: 8000 }]
envFrom: [{ secretRef: { name: django-env } }]
env: [{ name: DATABASE_URL, value: "postgres://app:$(DB_PASSWORD)@127.0.0.1:5432/app" }]
readinessProbe:
httpGet: { path: /health/, port: 8000, httpHeaders: [{ name: Host, value: example.com }] }
resources: { requests: { cpu: 500m, memory: 512Mi } }
Running the proxy as a native sidecar (an init container with restartPolicy: Always) means it starts before Django and stops after it, which also makes migration Jobs complete cleanly.
4. HTTPS with a Gateway
Use the Gateway API with a gke-l7-global-external-managed GatewayClass and a Google-managed certificate (Certificate Manager), or the classic Ingress with a ManagedCertificate. Set the load balancer health check to /health/; GKE derives it from the readiness probe or from a HealthCheckPolicy. In Django:
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
ALLOWED_HOSTS = ["example.com"]
5. Migrations as a Job
Run a Job with the same image, service account, and sidecar before updating the Deployment:
kubectl -n app create job migrate-$SHA --from=cronjob/migrate-template # or apply a Job manifest
kubectl -n app wait --for=condition=complete job/migrate-$SHA --timeout=600s
kubectl -n app set image deploy/web web=europe-west1-docker.pkg.dev/$PROJECT/apps/myapp:$SHA
See Run Django Migrations as a Kubernetes Job.
Verification check: kubectl -n app rollout status deploy/web succeeds, the Gateway shows a healthy backend, https://example.com serves with a Google-managed certificate, and Cloud SQL's connections view shows connections from the proxy.
Explanation
GKE Autopilot removes node management: you declare pods and their resource requests, and Google provisions capacity. Django needs three integrations to run well there: a secure path to the database (the Cloud SQL Auth Proxy handles IAM authentication and encryption, avoiding public IPs and allow-lists), credentials without key files (Workload Identity maps Kubernetes service accounts to Google service accounts), and HTTPS ingress (Google's load balancers with managed certificates). Everything else, including Celery workers as separate Deployments and Beat as a single replica, follows standard Kubernetes patterns.
Edge cases / notes
- Private IP. Cloud SQL with private IP plus the proxy's
--private-ipflag keeps traffic inside the VPC. - Celery runs as its own Deployment with the same sidecar; Beat must have exactly one replica.
- Cost. Autopilot charges for requested resources; idle replicas still cost money. Cloud Run may be cheaper for low traffic.
- Static files can be served by WhiteNoise or uploaded to GCS behind Cloud CDN.
Next steps
- General Kubernetes patterns: Deploy Django on Kubernetes.
- For request-driven workloads, compare Deploy Django on Google Cloud Run.
FAQ
Should I use GKE Autopilot or Standard for Django?
Autopilot for most teams: no node management and per-pod billing. Standard if you need node-level control.
How does Django connect to Cloud SQL on GKE?
Through the Cloud SQL Auth Proxy as a sidecar, authenticated with Workload Identity, connecting to 127.0.0.1.
Do I need service account key files?
No. Workload Identity gives pods Google credentials without keys.
When is Cloud Run better than GKE?
For a single web service with request-driven traffic and no long-running workers.