skills/jezweb/claude-skills/django-cloud-sql-postgres

django-cloud-sql-postgres

SKILL.md

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


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:

  1. Verify beta_settings.cloud_sql_instances in app.yaml
  2. Check connection name format: PROJECT:REGION:INSTANCE
  3. Ensure Cloud SQL instance exists: gcloud sql instances list

Problem: Connection works locally but fails on App Engine

Solution:

  1. Verify HOST uses Unix socket path, not 127.0.0.1
  2. Check environment variables are set in app.yaml
  3. Verify App Engine service account has Cloud SQL Client role

Problem: Migrations timeout during deployment

Solution:

  1. Don't run migrations in app.yaml entrypoint
  2. Use Cloud Build or run locally with proxy before deploying
  3. For large migrations, increase Cloud SQL tier temporarily

Problem: Static files 404 after deploy

Solution:

  1. Run python manage.py collectstatic --noinput before deploy
  2. Verify static/ handler in app.yaml points to correct directory
  3. 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?

  1. Check the troubleshooting section above
  2. Verify all environment variables are set correctly
  3. Check official docs: https://cloud.google.com/python/django/appengine
  4. Ensure Cloud SQL instance is running: gcloud sql instances list

Last verified: 2026-01-24 | Skill version: 1.0.0

Weekly Installs
21
Installed on
claude-code17
antigravity16
gemini-cli16
opencode13
cursor12
windsurf10