Deploy Django on Google Compute Engine

Deploy Django on a Google Compute Engine VM: create an e2 instance with a service account, install Gunicorn and Nginx, connect to Cloud SQL through the Auth Proxy, store media in Cloud Storage, open firewall rules, and add HTTPS.

Problem statement

Cloud Run is the default suggestion for Django on Google Cloud, but sometimes you want a plain virtual machine: long-running Celery workers and beat on the same host, WebSockets without request time limits, a predictable monthly bill, or simply a server you can SSH into. Compute Engine gives you that VM. The Google-specific parts are the service account, firewall rules, Cloud SQL connectivity, and Cloud Storage for media; the rest is a standard Gunicorn and Nginx setup.

Django on Compute EngineVM with SAe2 instanceGunicorn + Nginxsystemd unitsCloud SQLvia Auth ProxyCloud Storagemedia filesA VM runs the processes; Google services handle the database and files.
  1. Create a dedicated service account with Cloud SQL Client and Storage Object Admin roles.
  2. Create an e2-small or larger Debian or Ubuntu VM using that service account, with a static external IP.
  3. Allow HTTP and HTTPS with firewall rules (network tags http-server and https-server), and SSH only through IAP.
  4. Install Python, Gunicorn, and Nginx; run Gunicorn with systemd.
  5. Run the Cloud SQL Auth Proxy as a systemd service, point Django at 127.0.0.1:5432, and store media with django-storages[google].

Step-by-step solution

1. Create the service account and VM

gcloud iam service-accounts create django-vm
PROJECT=$(gcloud config get-value project)
for role in roles/cloudsql.client roles/storage.objectAdmin roles/secretmanager.secretAccessor; do
  gcloud projects add-iam-policy-binding $PROJECT \
    --member="serviceAccount:django-vm@$PROJECT.iam.gserviceaccount.com" --role=$role
done

gcloud compute addresses create django-ip --region=europe-west1
gcloud compute instances create django-prod \
  --zone=europe-west1-b --machine-type=e2-small \
  --image-family=debian-12 --image-project=debian-cloud \
  --service-account=django-vm@$PROJECT.iam.gserviceaccount.com \
  --scopes=cloud-platform \
  --address=django-ip --tags=http-server,https-server

The VM's service account lets Django, the Auth Proxy, and Cloud Storage use Application Default Credentials. No JSON key file on disk.

2. Lock down access

gcloud compute firewall-rules create allow-http --allow=tcp:80 --target-tags=http-server
gcloud compute firewall-rules create allow-https --allow=tcp:443 --target-tags=https-server
gcloud compute firewall-rules create allow-iap-ssh --allow=tcp:22 --source-ranges=35.235.240.0/20

Connect with gcloud compute ssh django-prod --tunnel-through-iap. The default network's rule allowing SSH from anywhere should be removed.

what runs on the instanceNginx :443TLS, static filesGunicorn (systemd)unix socketCelery worker and beatoptional unitsCloud SQL Auth Proxy127.0.0.1:5432VM service accountcredentials for allOne identity and one proxy replace passwords and IP allowlistsdjango-deployment.com
Every process authenticates as the VM, so there is no key file to rotate.

3. Install the app

sudo apt-get update && sudo apt-get install -y python3-venv nginx git
sudo useradd --system --create-home --home-dir /srv/app deploy
sudo -u deploy git clone https://github.com/acme/shop.git /srv/app/src
sudo -u deploy python3 -m venv /srv/app/.venv
sudo -u deploy /srv/app/.venv/bin/pip install -r /srv/app/src/requirements.txt gunicorn

Set up Gunicorn with a systemd unit and Nginx as a reverse proxy, as in Deploy Django With Gunicorn and Nginx on Ubuntu. On Debian 12, the same steps apply.

4. Connect to Cloud SQL

Install the Auth Proxy and run it as a service:

# /etc/systemd/system/cloud-sql-proxy.service
[Unit]
Description=Cloud SQL Auth Proxy
After=network-online.target

[Service]
ExecStart=/usr/local/bin/cloud-sql-proxy --private-ip --port 5432 acme:europe-west1:shop-db
Restart=always
User=deploy

[Install]
WantedBy=multi-user.target
DATABASE_URL=postgres://shop:...@127.0.0.1:5432/shop

The proxy authenticates with the VM's service account and encrypts traffic. --private-ip uses the instance's VPC path when Cloud SQL has a private IP. Add After=cloud-sql-proxy.service to the Gunicorn unit.

5. Media, secrets, and HTTPS

  • Media: django-storages[google] with GS_BUCKET_NAME; credentials come from the VM automatically.
  • Secrets: read from Secret Manager at startup, or write them to an EnvironmentFile during deploy.
  • HTTPS: point your domain at the static IP and run Certbot with the Nginx plugin.
which Google Cloud option?What does the app need?stateless HTTP, spikyCloud Runscale to zeroworkers, WebSockets, SSHCompute Engineone VM, systemdmany services, teamsGKEKubernetesA VM is often simplest when everything should run on one hostdjango-deployment.com
Cloud SQL and Cloud Storage work the same way with all three.

Explanation

A Compute Engine VM is a Linux server like any other, so Django runs exactly as it would on a VPS. What makes it "Google Cloud native" is identity: the attached service account provides credentials through the metadata server to every Google client library and to the Auth Proxy. That replaces database passwords over the internet, IP allowlists for Cloud SQL, and JSON keys for Cloud Storage, which are the most common security mistakes when running Django on GCE.

Edge cases / notes

  • e2-micro free tier. The always-free e2-micro has too little memory for more than one or two Gunicorn workers plus Celery; add swap or size up.
  • OS Login. Enabling OS Login ties SSH access to IAM roles, which is easier to manage than project-wide SSH keys.
  • Managed instance groups. For several VMs behind a load balancer, bake an image or use a startup script, and move sessions and cache to Memorystore.

Next steps

FAQ

Which machine type should I use?

e2-small (2 GB) is a reasonable start for a small Django app with Nginx and Gunicorn. Add memory for Celery or more workers.

Do I need a JSON service account key?

No. Attach the service account to the VM and use Application Default Credentials.

How should the VM reach Cloud SQL?

Through the Cloud SQL Auth Proxy, ideally over private IP. Avoid authorizing public IPs on the database.

Is Compute Engine cheaper than Cloud Run?

For steady traffic around the clock, a small VM is often cheaper. For spiky or low traffic, Cloud Run can cost less.

Keep going