Django on Google Cloud SQL PostgreSQL
Status: Production Ready Last Updated: 2026-01-24 Dependencies: None Latest Versions: Django@5.1, psycopg2-binary@2.9.9, gunicorn@23.0.0, google-cloud-sql-connector@1.12.0
Quick Start (10 Minutes) 1. Install Dependencies pip install Django psycopg2-binary gunicorn
For Cloud SQL Python Connector (recommended for local dev):
pip install "cloud-sql-python-connector[pg8000]"
Why this matters:
psycopg2-binary is the PostgreSQL adapter for Django gunicorn is required for App Engine Standard (Python 3.10+) Cloud SQL Python Connector provides secure connections without SSH tunneling 2. Configure Django Settings
settings.py (production with Unix socket):
import os
Detect App Engine environment
IS_APP_ENGINE = os.getenv('GAE_APPLICATION', None)
if IS_APP_ENGINE: # Production: Connect via Unix socket DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ['DB_NAME'], 'USER': os.environ['DB_USER'], 'PASSWORD': os.environ['DB_PASSWORD'], 'HOST': f"/cloudsql/{os.environ['CLOUD_SQL_CONNECTION_NAME']}", 'PORT': '', # Empty for Unix socket } } else: # Local development: Connect via Cloud SQL Proxy DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ.get('DB_NAME', 'mydb'), 'USER': os.environ.get('DB_USER', 'postgres'), 'PASSWORD': os.environ.get('DB_PASSWORD', ''), 'HOST': '127.0.0.1', 'PORT': '5432', } }
CRITICAL:
App Engine connects via Unix socket at /cloudsql/PROJECT:REGION:INSTANCE Local development requires Cloud SQL Auth Proxy on 127.0.0.1:5432 Never hardcode connection strings - use environment variables 3. Create app.yaml runtime: python310 entrypoint: gunicorn -b :$PORT myproject.wsgi:application
env_variables: DB_NAME: "mydb" DB_USER: "postgres" CLOUD_SQL_CONNECTION_NAME: "project-id:region:instance-name"
Cloud SQL connection
beta_settings: cloud_sql_instances: "project-id:region:instance-name"
handlers: - url: /static static_dir: static/ - url: /.* script: auto secure: always
CRITICAL:
beta_settings.cloud_sql_instances enables the Unix socket at /cloudsql/... DB_PASSWORD should be set via gcloud app deploy or Secret Manager, not in app.yaml The 6-Step Setup Process Step 1: Create Cloud SQL Instance
Create PostgreSQL instance
gcloud sql instances create myinstance \ --database-version=POSTGRES_15 \ --tier=db-f1-micro \ --region=us-central1
Create database
gcloud sql databases create mydb --instance=myinstance
Create user
gcloud sql users create postgres \ --instance=myinstance \ --password=YOUR_SECURE_PASSWORD
Key Points:
Use POSTGRES_15 or later for best compatibility db-f1-micro is cheapest for dev ($7-10/month), use db-g1-small or higher for production Note the connection name: PROJECT_ID:REGION:INSTANCE_NAME Step 2: Configure Django Project
requirements.txt:
Django>=5.1,<6.0 psycopg2-binary>=2.9.9 gunicorn>=23.0.0 whitenoise>=6.7.0
settings.py additions:
import os
Security settings for production
DEBUG = os.environ.get('DEBUG', 'False') == 'True' ALLOWED_HOSTS = [ '.appspot.com', '.run.app', 'localhost', '127.0.0.1', ]
Static files with WhiteNoise
STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'static') MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware') STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
Database connection pooling
DATABASES['default']['CONN_MAX_AGE'] = 60 # Keep connections open for 60 seconds
Why these settings:
CONN_MAX_AGE=60 reduces connection overhead (Cloud SQL has connection limits) WhiteNoise serves static files without Cloud Storage ALLOWED_HOSTS must include .appspot.com for App Engine Step 3: Set Up Local Development with Cloud SQL Proxy
Install Cloud SQL Auth Proxy:
macOS
brew install cloud-sql-proxy
Linux
curl -o cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.14.1/cloud-sql-proxy.linux.amd64 chmod +x cloud-sql-proxy
Run the proxy:
Authenticate first
gcloud auth application-default login
Start proxy (runs on 127.0.0.1:5432)
./cloud-sql-proxy PROJECT_ID:REGION:INSTANCE_NAME
Or with specific port
./cloud-sql-proxy PROJECT_ID:REGION:INSTANCE_NAME --port=5432
Set environment variables for local dev:
export DB_NAME=mydb export DB_USER=postgres export DB_PASSWORD=your_password export DEBUG=True
Key Points:
Proxy creates a secure tunnel to Cloud SQL No need to whitelist your IP address Works with both password and IAM authentication Step 4: Run Migrations
Local (with proxy running)
python manage.py migrate
Verify connection
python manage.py dbshell
For production migrations (via Cloud Build or local with proxy):
Option 1: Run locally with proxy
./cloud-sql-proxy PROJECT:REGION:INSTANCE & python manage.py migrate
Option 2: Use Cloud Build (recommended)
See references/cloud-build-migrations.md
Step 5: Configure Gunicorn
gunicorn.conf.py (optional, for fine-tuning):
import multiprocessing
Workers
workers = 2 # App Engine Standard limits this threads = 4 worker_class = 'gthread'
Timeout (App Engine has 60s limit for standard, 3600s for flexible)
timeout = 55
Logging
accesslog = '-' errorlog = '-' loglevel = 'info'
Bind (App Engine sets $PORT)
bind = f"0.0.0.0:{os.environ.get('PORT', '8080')}"
app.yaml entrypoint options:
Simple (recommended for most cases)
entrypoint: gunicorn -b :$PORT myproject.wsgi:application
With config file
entrypoint: gunicorn -c gunicorn.conf.py myproject.wsgi:application
With workers and timeout
entrypoint: gunicorn -b :$PORT -w 2 -t 55 myproject.wsgi:application
Key Points:
App Engine Standard limits workers (F1: 1 worker, F2: 2 workers) Use gthread worker class for I/O-bound Django apps Set timeout < 60s to avoid App Engine killing requests Step 6: Deploy to App Engine
Collect static files
python manage.py collectstatic --noinput
Deploy
gcloud app deploy app.yaml
Set DB password via environment
gcloud app deploy --set-env-vars="DB_PASSWORD=your_secure_password"
View logs
gcloud app logs tail -s default
Verify deployment:
Open in browser
gcloud app browse
Check database connection
gcloud app logs read --service=default | grep -i database
Critical Rules (Django + Cloud SQL)
ALWAYS DO:
Use Unix socket path /cloudsql/PROJECT:REGION:INSTANCE on App Engine Set PORT='' (empty string) for Unix socket connections Use Cloud SQL Auth Proxy for local development Set CONN_MAX_AGE for connection pooling (30-60 seconds) Use environment variables for database credentials Use Secret Manager for production passwords
NEVER DO:
Put database password in app.yaml (use Secret Manager or env vars at deploy time) Use HOST='localhost' on App Engine (must use Unix socket path) Forget beta_settings.cloud_sql_instances in app.yaml Set CONN_MAX_AGE=None (unlimited) - can exhaust connection pool Skip SSL on Cloud SQL (it's enforced by default) Known Issues Prevention
This skill prevents 12 documented issues:
Issue #1: OperationalError - No such file or directory
Error: django.db.utils.OperationalError: could not connect to server: No such file or directory Source: https://cloud.google.com/sql/docs/postgres/connect-app-engine-standard Why It Happens: App Engine can't find the Unix socket because beta_settings.cloud_sql_instances is missing in app.yaml Prevention: Always include beta_settings.cloud_sql_instances: "PROJECT:REGION:INSTANCE" in app.yaml
Issue #2: Connection Refused on Local Development
Error: django.db.utils.OperationalError: connection refused or could not connect to server: Connection refused Source: https://cloud.google.com/sql/docs/postgres/connect-auth-proxy Why It Happens: Cloud SQL Auth Proxy is not running or bound to wrong port Prevention: Start cloud-sql-proxy PROJECT:REGION:INSTANCE before running Django locally. Verify it's running on port 5432.
Issue #3: FATAL: password authentication failed
Error: FATAL: password authentication failed for user "postgres" Source: https://cloud.google.com/sql/docs/postgres/create-manage-users Why It Happens: Wrong password in environment variable, or user doesn't exist in Cloud SQL instance Prevention: Verify password with gcloud sql users list --instance=INSTANCE. Reset if needed: gcloud sql users set-password postgres --instance=INSTANCE --password=NEW_PASSWORD
Issue #4: Too Many Connections
Error: FATAL: too many connections for role "postgres" or remaining connection slots are reserved Source: https://cloud.google.com/sql/docs/postgres/quotas#connection_limits Why It Happens: Each request opens a new connection without pooling, exhausting the 25-100 connection limit Prevention: Set CONN_MAX_AGE = 60 in Django settings. For high traffic, use PgBouncer or django-db-connection-pool.
Issue #5: App Engine Request Timeout
Error: DeadlineExceededError or request terminated after 60 seconds Source: https://cloud.google.com/appengine/docs/standard/python3/how-requests-are-handled Why It Happens: Database query or migration takes longer than App Engine's 60-second limit Prevention: Set Gunicorn timeout to 55 seconds. For long-running tasks, use Cloud Tasks or Cloud Run Jobs.
Issue #6: Static Files 404 in Production
Error: Static files return 404, CSS/JS not loading Source: https://cloud.google.com/appengine/docs/standard/serving-static-files Why It Happens: Missing static/ handler in app.yaml or collectstatic not run before deploy Prevention: Run python manage.py collectstatic --noinput before deploy. Include static handler in app.yaml.
Issue #7: CSRF Verification Failed
Error: Forbidden (403) CSRF verification failed Source: Django documentation on CSRF Why It Happens: CSRF_TRUSTED_ORIGINS not set for appspot.com domain Prevention: Add CSRF_TRUSTED_ORIGINS = ['https://*.appspot.com'] to settings.py
Issue #8: Database Not Found After Deployment
Error: django.db.utils.OperationalError: FATAL: database "mydb" does not exist Source: https://cloud.google.com/sql/docs/postgres/create-manage-databases Why It Happens: Database exists in Cloud SQL but DB_NAME environment variable is wrong Prevention: Verify database name: gcloud sql databases list --instance=INSTANCE. Ensure DB_NAME matches exactly.
Issue #9: IAM Authentication Failure
Error: FATAL: Cloud SQL IAM user authentication failed Source: https://cloud.google.com/sql/docs/postgres/iam-logins Why It Happens: App Engine service account doesn't have roles/cloudsql.instanceUser role Prevention: Grant role: gcloud projects add-iam-policy-binding PROJECT --member="serviceAccount:PROJECT@appspot.gserviceaccount.com" --role="roles/cloudsql.instanceUser"
Issue #10: Migrations Fail in Production
Error: Migrations timeout or can't run during deployment Source: https://cloud.google.com/sql/docs/postgres/connect-build Why It Happens: App Engine deploy doesn't provide a migration step; running in entrypoint times out Prevention: Run migrations separately via Cloud Build, or locally with Cloud SQL Proxy before deploying.
Issue #11: SECRET_KEY Exposed
Error: Security warning about hardcoded SECRET_KEY Source: Django deployment checklist Why It Happens: SECRET_KEY is in settings.py instead of environment variable or Secret Manager Prevention: Use SECRET_KEY = os.environ.get('SECRET_KEY') and set via gcloud app deploy --set-env-vars or Secret Manager.
Issue #12: Cold Start Database Timeout
Error: First request after idle period times out Source: https://cloud.google.com/appengine/docs/standard/how-instances-are-managed Why It Happens: App Engine instance cold start + Cloud SQL activation delay (if using "on demand" activation) Prevention: Use App Engine warmup requests, or keep Cloud SQL instance "always on" (increases cost). Set app_engine_apis: true and add /_ah/warmup handler.
Configuration Files Reference settings.py (Complete Production Example) import os from pathlib import Path
BASE_DIR = Path(file).resolve().parent.parent
Security
SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-key-change-in-production') DEBUG = os.environ.get('DEBUG', 'False') == 'True' ALLOWED_HOSTS = [ '.appspot.com', '.run.app', 'localhost', '127.0.0.1', ]
CSRF for App Engine
CSRF_TRUSTED_ORIGINS = [ 'https://.appspot.com', 'https://.run.app', ]
Application definition
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # Your apps here ]
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', # Static files 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
ROOT_URLCONF = 'myproject.urls'
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [BASE_DIR / 'templates'], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
WSGI_APPLICATION = 'myproject.wsgi.application'
Database
IS_APP_ENGINE = os.getenv('GAE_APPLICATION', None)
if IS_APP_ENGINE: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ['DB_NAME'], 'USER': os.environ['DB_USER'], 'PASSWORD': os.environ['DB_PASSWORD'], 'HOST': f"/cloudsql/{os.environ['CLOUD_SQL_CONNECTION_NAME']}", 'PORT': '', 'CONN_MAX_AGE': 60, 'OPTIONS': { 'connect_timeout': 10, }, } } else: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ.get('DB_NAME', 'mydb'), 'USER': os.environ.get('DB_USER', 'postgres'), 'PASSWORD': os.environ.get('DB_PASSWORD', ''), 'HOST': os.environ.get('DB_HOST', '127.0.0.1'), 'PORT': os.environ.get('DB_PORT', '5432'), 'CONN_MAX_AGE': 60, } }
Static files
STATIC_URL = '/static/' STATIC_ROOT = BASE_DIR / 'static' STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
Default primary key field type
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
Logging
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'class': 'logging.StreamHandler', }, }, 'root': { 'handlers': ['console'], 'level': 'INFO', }, 'loggers': { 'django': { 'handlers': ['console'], 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'), 'propagate': False, }, }, }
app.yaml (Complete Example) runtime: python310 entrypoint: gunicorn -b :$PORT -w 2 -t 55 --threads 4 myproject.wsgi:application
instance_class: F2
env_variables: DB_NAME: "mydb" DB_USER: "postgres" CLOUD_SQL_CONNECTION_NAME: "project-id:us-central1:instance-name" DEBUG: "False"
beta_settings: cloud_sql_instances: "project-id:us-central1:instance-name"
handlers: - url: /static static_dir: static/ secure: always
- url: /.* script: auto secure: always
automatic_scaling: min_instances: 0 max_instances: 2 target_cpu_utilization: 0.65
Why these settings:
F2 instance class allows 2 Gunicorn workers min_instances: 0 saves costs when idle target_cpu_utilization: 0.65 scales before overload requirements.txt Django>=5.1,<6.0 psycopg2-binary>=2.9.9 gunicorn>=23.0.0 whitenoise>=6.7.0
Common Patterns Pattern 1: Environment-Aware Database Configuration import os
def get_database_config(): """Return database config based on environment.""" is_production = os.getenv('GAE_APPLICATION', None)
if is_production:
return {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ['DB_NAME'],
'USER': os.environ['DB_USER'],
'PASSWORD': os.environ['DB_PASSWORD'],
'HOST': f"/cloudsql/{os.environ['CLOUD_SQL_CONNECTION_NAME']}",
'PORT': '',
'CONN_MAX_AGE': 60,
}
else:
return {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DB_NAME', 'mydb'),
'USER': os.environ.get('DB_USER', 'postgres'),
'PASSWORD': os.environ.get('DB_PASSWORD', ''),
'HOST': '127.0.0.1',
'PORT': '5432',
'CONN_MAX_AGE': 60,
}
DATABASES = {'default': get_database_config()}
When to use: Standard Django project needing local/production parity
Pattern 2: Secret Manager Integration from google.cloud import secretmanager
def get_secret(secret_id, version_id='latest'): """Retrieve secret from Google Secret Manager.""" client = secretmanager.SecretManagerServiceClient() project_id = os.environ.get('GOOGLE_CLOUD_PROJECT') name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}" response = client.access_secret_version(request={"name": name}) return response.payload.data.decode('UTF-8')
Usage in settings.py
if os.getenv('GAE_APPLICATION'): SECRET_KEY = get_secret('django-secret-key') DB_PASSWORD = get_secret('db-password')
When to use: Production deployments requiring proper secret management
Pattern 3: Cloud SQL Python Connector (Alternative to Proxy)
For local development without Cloud SQL Auth Proxy
from google.cloud.sql.connector import Connector
def get_db_connection(): connector = Connector() return connector.connect( "project:region:instance", "pg8000", user="postgres", password=os.environ["DB_PASSWORD"], db="mydb", )
In settings.py for local dev (requires pg8000 driver)
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'mydb', 'USER': 'postgres', 'OPTIONS': { 'get_conn': get_db_connection, }, } }
When to use: Local development when you can't install Cloud SQL Auth Proxy
Pattern 4: Health Check Endpoint with Database Verification
views.py
from django.http import JsonResponse from django.db import connection
def health_check(request): """Health check endpoint for App Engine.""" try: with connection.cursor() as cursor: cursor.execute("SELECT 1") return JsonResponse({ 'status': 'healthy', 'database': 'connected', }) except Exception as e: return JsonResponse({ 'status': 'unhealthy', 'database': str(e), }, status=503)
urls.py
urlpatterns = [ path('_ah/health', health_check, name='health_check'), ]
When to use: Load balancer health checks, monitoring database connectivity
Using Bundled Resources Templates (templates/) templates/settings_snippet.py - Copy-paste database configuration templates/app.yaml - Complete App Engine configuration templates/requirements.txt - Production dependencies References (references/) references/cloud-sql-proxy-setup.md - Detailed proxy installation and usage references/iam-authentication.md - IAM-based authentication (no passwords) references/secret-manager.md - Storing secrets properly references/migrations-in-production.md - Running migrations safely
When Claude should load these:
Load cloud-sql-proxy-setup.md when user has local connection issues Load iam-authentication.md when setting up passwordless auth Load migrations-in-production.md before first production deployment Advanced Topics IAM Database Authentication
Instead of password authentication, use IAM for service-to-service auth:
Enable IAM authentication on instance
gcloud sql instances patch INSTANCE --database-flags cloudsql.iam_authentication=on
Create IAM user
gcloud sql users create SERVICE_ACCOUNT@PROJECT.iam \ --instance=INSTANCE \ --type=CLOUD_IAM_SERVICE_ACCOUNT
Grant connect permission
gcloud projects add-iam-policy-binding PROJECT \ --member="serviceAccount:PROJECT@appspot.gserviceaccount.com" \ --role="roles/cloudsql.instanceUser"
Django settings for IAM auth:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ['DB_NAME'], 'USER': f"{os.environ['SERVICE_ACCOUNT']}@{os.environ['PROJECT_ID']}.iam", 'HOST': f"/cloudsql/{os.environ['CLOUD_SQL_CONNECTION_NAME']}", 'PORT': '', # No PASSWORD needed with IAM auth } }
Connection Pooling with PgBouncer
For high-traffic applications, deploy PgBouncer on Cloud Run:
Cloud Run service for PgBouncer
See references/pgbouncer-setup.md for full configuration
Why PgBouncer:
Cloud SQL limits connections (25-4000 depending on tier) Django's CONN_MAX_AGE helps but doesn't pool across processes PgBouncer provides true connection pooling Running Migrations Safely
Option 1: Cloud Build (Recommended)
cloudbuild.yaml
steps: - name: 'gcr.io/cloud-builders/gcloud' args: ['sql', 'connect', 'INSTANCE', '--quiet']
- name: 'python:3.10'
entrypoint: 'bash'
args:
- '-c'
- | pip install -r requirements.txt python manage.py migrate --noinput env:
- 'DB_NAME=mydb'
- 'DB_USER=postgres'
- 'DB_HOST=/cloudsql/PROJECT:REGION:INSTANCE' secretEnv: ['DB_PASSWORD']
availableSecrets: secretManager: - versionName: projects/PROJECT/secrets/db-password/versions/latest env: 'DB_PASSWORD'
Option 2: Local with Proxy (Simple)
./cloud-sql-proxy PROJECT:REGION:INSTANCE & python manage.py migrate
Dependencies
Required:
Django>=5.1 - Web framework psycopg2-binary>=2.9.9 - PostgreSQL adapter gunicorn>=23.0.0 - WSGI server for App Engine
Recommended:
whitenoise>=6.7.0 - Static file serving python-dotenv>=1.0.0 - Local environment variables
Optional:
google-cloud-secret-manager>=2.20.0 - Secret Manager integration cloud-sql-python-connector[pg8000]>=1.12.0 - Python-native Cloud SQL connector django-db-connection-pool>=1.2.5 - Connection pooling (alternative to CONN_MAX_AGE) Official Documentation Cloud SQL for PostgreSQL: https://cloud.google.com/sql/docs/postgres App Engine Python 3: https://cloud.google.com/appengine/docs/standard/python3 Cloud SQL Auth Proxy: https://cloud.google.com/sql/docs/postgres/connect-auth-proxy Django on App Engine: https://cloud.google.com/python/django/appengine Cloud SQL Python Connector: https://github.com/GoogleCloudPlatform/cloud-sql-python-connector Secret Manager: https://cloud.google.com/secret-manager/docs Package Versions (Verified 2026-01-24) { "dependencies": { "Django": ">=5.1,<6.0", "psycopg2-binary": ">=2.9.9", "gunicorn": ">=23.0.0", "whitenoise": ">=6.7.0" }, "optional": { "google-cloud-secret-manager": ">=2.20.0", "cloud-sql-python-connector": ">=1.12.0" } }
Production Example
This skill is based on production Django deployments on App Engine:
Use Case: Multi-tenant SaaS application Traffic: 10K+ daily requests Database: Cloud SQL PostgreSQL (db-g1-small, 25 connections) Errors: 0 (all 12 known issues prevented) Validation: Unix socket connection, connection pooling, static files, CSRF Troubleshooting Problem: No such file or directory for socket
Solution:
Verify beta_settings.cloud_sql_instances in app.yaml Check connection name format: PROJECT:REGION:INSTANCE Ensure Cloud SQL instance exists: gcloud sql instances list Problem: Connection works locally but fails on App Engine
Solution:
Verify HOST uses Unix socket path, not 127.0.0.1 Check environment variables are set in app.yaml Verify App Engine service account has Cloud SQL Client role Problem: Migrations timeout during deployment
Solution:
Don't run migrations in app.yaml entrypoint Use Cloud Build or run locally with proxy before deploying For large migrations, increase Cloud SQL tier temporarily Problem: Static files 404 after deploy
Solution:
Run python manage.py collectstatic --noinput before deploy Verify static/ handler in app.yaml points to correct directory Check WhiteNoise is in MIDDLEWARE list Complete Setup Checklist Cloud SQL PostgreSQL instance created Database and user created in Cloud SQL Cloud SQL Auth Proxy installed locally Django settings configured for Unix socket (production) and localhost (dev) beta_settings.cloud_sql_instances in app.yaml CONN_MAX_AGE set for connection pooling Environment variables configured (DB_NAME, DB_USER, etc.) DB_PASSWORD stored securely (not in app.yaml) Static files collected and handler configured CSRF_TRUSTED_ORIGINS includes *.appspot.com Migrations run (locally with proxy) before first deploy Deployed with gcloud app deploy Verified database connection in production logs
Questions? Issues?
Check the troubleshooting section above Verify all environment variables are set correctly Check official docs: https://cloud.google.com/python/django/appengine Ensure Cloud SQL instance is running: gcloud sql instances list
Last verified: 2026-01-24 | Skill version: 1.0.0