Agent skill

google-app-engine

Deploy Python applications to Google App Engine Standard/Flexible. Covers app.yaml configuration, Cloud SQL socket connections, Cloud Storage for static files, scaling settings, and environment variables. Use when: deploying to App Engine, configuring app.yaml, connecting Cloud SQL, setting up static file serving, or troubleshooting 502 errors, cold starts, or memory limits.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/google-app-engine

SKILL.md

Google App Engine

Status: Production Ready Last Updated: 2026-01-24 Dependencies: Google Cloud SDK (gcloud CLI) Skill Version: 1.0.0


Quick Start (10 Minutes)

1. Prerequisites

bash
# Install Google Cloud SDK
# macOS
brew install google-cloud-sdk

# Or download from https://cloud.google.com/sdk/docs/install

# Authenticate
gcloud auth login
gcloud config set project YOUR_PROJECT_ID

# Enable required APIs
gcloud services enable appengine.googleapis.com
gcloud services enable sqladmin.googleapis.com
gcloud services enable secretmanager.googleapis.com

2. Create app.yaml

yaml
# app.yaml - Standard Environment (Python 3.12)
runtime: python312
instance_class: F2

env_variables:
  DJANGO_SETTINGS_MODULE: "myproject.settings.production"

handlers:
  # Static files (served directly by App Engine)
  - url: /static
    static_dir: staticfiles/
    secure: always

  # All other requests go to the app
  - url: /.*
    script: auto
    secure: always

automatic_scaling:
  min_instances: 0
  max_instances: 10
  target_cpu_utilization: 0.65

3. Deploy

bash
# Deploy to App Engine
gcloud app deploy

# Deploy with specific version
gcloud app deploy --version=v1 --no-promote

# View logs
gcloud app logs tail -s default

Standard vs Flexible Environment

Standard Environment (Recommended for Most Apps)

Use when: Building typical web apps, APIs, or services that fit within runtime constraints.

Aspect Standard
Startup Fast (milliseconds)
Scaling Scale to zero
Pricing Pay per request
Runtimes Python 3.8-3.12
Instance Classes F1, F2, F4, F4_1G
Max Request 60 seconds
Background Cloud Tasks only
yaml
# app.yaml - Standard
runtime: python312
instance_class: F2

Flexible Environment

Use when: Need custom runtimes, Docker, longer request timeouts, or background threads.

Aspect Flexible
Startup Slower (minutes)
Scaling Min 1 instance
Pricing Per-hour VM
Runtimes Any (Docker)
Max Request 60 minutes
Background Native threads
yaml
# app.yaml - Flexible
runtime: python
env: flex

runtime_config:
  runtime_version: "3.12"

resources:
  cpu: 1
  memory_gb: 0.5
  disk_size_gb: 10

automatic_scaling:
  min_num_instances: 1
  max_num_instances: 5

Cost Warning: Flexible always runs at least 1 instance (~$30-40/month minimum).


Cloud SQL Connection

Standard Environment (Unix Socket)

App Engine Standard connects to Cloud SQL via Unix sockets, not TCP/IP.

python
# settings.py
import os

if os.getenv('GAE_APPLICATION'):
    # Production: Cloud SQL 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: Cloud SQL Proxy or local DB
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': os.environ.get('DB_NAME', 'localdb'),
            'USER': os.environ.get('DB_USER', 'postgres'),
            'PASSWORD': os.environ.get('DB_PASSWORD', ''),
            'HOST': '127.0.0.1',
            'PORT': '5432',
        }
    }
yaml
# app.yaml
env_variables:
  DB_NAME: "mydb"
  DB_USER: "myuser"
  CLOUD_SQL_CONNECTION_NAME: "project:region:instance"

# CRITICAL: Beta settings for Cloud SQL socket
beta_settings:
  cloud_sql_instances: "project:region:instance"

CRITICAL: The beta_settings.cloud_sql_instances enables the Unix socket. Without it, connection fails.

Local Development with Cloud SQL Proxy

bash
# Download and run Cloud SQL Proxy
cloud-sql-proxy PROJECT:REGION:INSTANCE --port=5432

# Or use Docker
docker run -p 5432:5432 \
  gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.8.0 \
  PROJECT:REGION:INSTANCE

Static Files with Cloud Storage

Option 1: App Engine Static Handlers (Simple)

yaml
# app.yaml
handlers:
  - url: /static
    static_dir: staticfiles/
    secure: always
    expiration: "1d"
bash
# Collect static files before deploy
python manage.py collectstatic --noinput
gcloud app deploy

Limitation: Files bundled with deploy, limited to 32MB per file.

Option 2: Cloud Storage (Recommended for Production)

python
# settings.py
from google.cloud import storage

GS_BUCKET_NAME = os.environ.get('GS_BUCKET_NAME')
STATICFILES_STORAGE = 'storages.backends.gcloud.GoogleCloudStorage'
DEFAULT_FILE_STORAGE = 'storages.backends.gcloud.GoogleCloudStorage'

# Or with django-storages
STORAGES = {
    "default": {
        "BACKEND": "storages.backends.gcloud.GoogleCloudStorage",
        "OPTIONS": {
            "bucket_name": GS_BUCKET_NAME,
            "location": "media",
        },
    },
    "staticfiles": {
        "BACKEND": "storages.backends.gcloud.GoogleCloudStorage",
        "OPTIONS": {
            "bucket_name": GS_BUCKET_NAME,
            "location": "static",
        },
    },
}
bash
# Install django-storages
pip install django-storages[google]

# Create bucket with public access for static files
gsutil mb -l us-central1 gs://YOUR_BUCKET_NAME
gsutil iam ch allUsers:objectViewer gs://YOUR_BUCKET_NAME

Environment Variables and Secrets

Simple: app.yaml env_variables

yaml
# app.yaml - NOT for secrets!
env_variables:
  DJANGO_SETTINGS_MODULE: "myproject.settings.production"
  DEBUG: "False"

Warning: app.yaml is committed to source control. Never put secrets here.

Production: Secret Manager

python
# settings.py
from google.cloud import secretmanager

def get_secret(secret_id, version="latest"):
    """Fetch 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}"
    response = client.access_secret_version(request={"name": name})
    return response.payload.data.decode("UTF-8")

# Usage
if os.getenv('GAE_APPLICATION'):
    SECRET_KEY = get_secret('django-secret-key')
    DB_PASSWORD = get_secret('db-password')
bash
# Create secrets
echo -n "your-secret-key" | gcloud secrets create django-secret-key --data-file=-
echo -n "db-password" | gcloud secrets create db-password --data-file=-

# Grant App Engine access
gcloud secrets add-iam-policy-binding django-secret-key \
    --member="serviceAccount:YOUR_PROJECT@appspot.gserviceaccount.com" \
    --role="roles/secretmanager.secretAccessor"

Scaling Configuration

Automatic Scaling (Default)

yaml
automatic_scaling:
  min_instances: 0        # Scale to zero when idle
  max_instances: 10       # Cap maximum instances
  target_cpu_utilization: 0.65
  target_throughput_utilization: 0.6
  max_concurrent_requests: 80
  min_pending_latency: 30ms
  max_pending_latency: automatic

Basic Scaling (Background Tasks)

yaml
basic_scaling:
  max_instances: 5
  idle_timeout: 5m

Use for: Services that need to run background work without scaling to zero immediately.

Manual Scaling (Fixed Instances)

yaml
manual_scaling:
  instances: 3

Use for: Predictable workloads, WebSocket connections, or when you need guaranteed capacity.


Instance Classes

Class Memory CPU Cost/hour
F1 256 MB 600 MHz $0.05
F2 512 MB 1.2 GHz $0.10
F4 1 GB 2.4 GHz $0.20
F4_1G 2 GB 2.4 GHz $0.30
yaml
# Recommended for Django
instance_class: F2  # 512MB usually sufficient

Upgrade to F4 if you see memory errors or slow response times.


Known Issues Prevention

This skill prevents 6 documented issues:

Issue #1: Cloud SQL Connection Refused

Error: could not connect to server: Connection refused Why It Happens: Missing beta_settings.cloud_sql_instances in app.yaml Prevention: Always include:

yaml
beta_settings:
  cloud_sql_instances: "project:region:instance"

Issue #2: Static Files 404

Error: Static files return 404 after deploy Why It Happens: collectstatic not run, or handler order wrong Prevention:

bash
python manage.py collectstatic --noinput

And ensure static handler comes before catch-all:

yaml
handlers:
  - url: /static
    static_dir: staticfiles/
  - url: /.*
    script: auto

Issue #3: 502 Bad Gateway

Error: 502 errors on first request or under load Why It Happens: Cold start timeout, app takes too long to initialize Prevention:

  • Optimize app startup (lazy imports, connection pooling)
  • Use min_instances: 1 to avoid cold starts
  • Increase instance_class for more CPU/memory

Issue #4: Memory Limit Exceeded

Error: Exceeded soft memory limit in logs Why It Happens: F1 class (256MB) too small for Django + dependencies Prevention: Use instance_class: F2 minimum for Django apps

Issue #5: Request Timeout

Error: DeadlineExceededError after 60 seconds Why It Happens: Standard environment has 60s request limit Prevention:

  • Move long tasks to Cloud Tasks
  • Use Flexible environment for longer timeouts
  • Optimize database queries

Issue #6: Secret Key in Source Control

Error: Django SECRET_KEY exposed in git history Why It Happens: Putting secrets in app.yaml env_variables Prevention: Use Secret Manager (see Environment Variables section)


Deployment Commands

bash
# Deploy default service
gcloud app deploy

# Deploy specific service
gcloud app deploy app.yaml --service=api

# Deploy without promoting (for testing)
gcloud app deploy --version=v2 --no-promote

# Split traffic between versions
gcloud app services set-traffic default --splits=v1=0.5,v2=0.5

# Promote version
gcloud app versions migrate v2

# View logs
gcloud app logs tail -s default

# Open app in browser
gcloud app browse

# List versions
gcloud app versions list

# Delete old versions
gcloud app versions delete v1 v2 --quiet

Multi-Service Architecture

myproject/
├── app.yaml              # Default service
├── api/
│   └── app.yaml          # API service
├── worker/
│   └── app.yaml          # Background worker
└── dispatch.yaml         # URL routing
yaml
# dispatch.yaml
dispatch:
  - url: "*/api/*"
    service: api
  - url: "*/tasks/*"
    service: worker
bash
# Deploy all services
gcloud app deploy app.yaml api/app.yaml worker/app.yaml dispatch.yaml

Common Patterns

Health Check Endpoint

python
# urls.py
urlpatterns = [
    path('_ah/health', lambda r: HttpResponse('ok')),
    # ... other urls
]
yaml
# app.yaml
liveness_check:
  path: "/_ah/health"
  check_interval_sec: 30
  timeout_sec: 4
  failure_threshold: 2
  success_threshold: 2

readiness_check:
  path: "/_ah/health"
  check_interval_sec: 5
  timeout_sec: 4
  failure_threshold: 2
  success_threshold: 2

Warmup Requests

yaml
# app.yaml
inbound_services:
  - warmup
python
# urls.py
urlpatterns = [
    path('_ah/warmup', warmup_view),
]

# views.py
def warmup_view(request):
    """Pre-warm caches and connections."""
    from django.db import connection
    connection.ensure_connection()
    return HttpResponse('ok')

HTTPS Redirect

yaml
# app.yaml
handlers:
  - url: /.*
    script: auto
    secure: always  # Redirects HTTP to HTTPS

Local Development

Using Cloud SQL Proxy

bash
# Terminal 1: Run Cloud SQL Proxy
cloud-sql-proxy PROJECT:REGION:INSTANCE --port=5432

# Terminal 2: Run Django
export DB_HOST=127.0.0.1
export DB_PORT=5432
python manage.py runserver

Using dev_appserver (Legacy)

bash
# Not recommended - use Django's runserver instead
dev_appserver.py app.yaml

Environment Detection

python
# settings.py
import os

# Detect App Engine environment
IS_GAE = os.getenv('GAE_APPLICATION') is not None
IS_GAE_LOCAL = os.getenv('GAE_ENV') == 'localdev'

if IS_GAE:
    DEBUG = False
    ALLOWED_HOSTS = ['.appspot.com', '.your-domain.com']
else:
    DEBUG = True
    ALLOWED_HOSTS = ['localhost', '127.0.0.1']

Bundled Resources

Templates (templates/)

  • app.yaml - Standard environment configuration
  • app-flex.yaml - Flexible environment configuration
  • requirements.txt - Common dependencies for App Engine

References (references/)

  • instance-classes.md - Detailed instance class comparison
  • common-errors.md - Error messages and solutions

Official Documentation


Dependencies

# requirements.txt
gunicorn>=21.0.0
google-cloud-secret-manager>=2.16.0
google-cloud-storage>=2.10.0
django-storages[google]>=1.14.0
psycopg2-binary>=2.9.9  # For PostgreSQL

Production Checklist

  • SECRET_KEY in Secret Manager (not app.yaml)
  • DEBUG = False in production settings
  • ALLOWED_HOSTS configured for your domain
  • collectstatic runs before deploy
  • beta_settings.cloud_sql_instances set
  • Instance class appropriate (F2+ for Django)
  • HTTPS enforced (secure: always)
  • Health check endpoint configured
  • Error monitoring set up (Cloud Error Reporting)

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

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results