All topics
Cloud · Learning hub

App Engine notes for developers

Master App Engine with a curated set of 1 developer notes — core concepts, patterns, and interview prep. Maintained by the DevRecall team.

Save this stack to your DevRecallTest yourself — App Engine quizMore Cloud notes
App Engine

App Engine Essentials

App Engine Essentials What It Is & When To Use It Google App Engine is a fully managed Platform-as-a-Service (PaaS) for deploying web apps and APIs without touc

App Engine Essentials

What It Is & When To Use It

Google App Engine is a fully managed Platform-as-a-Service (PaaS) for deploying web apps and APIs without touching servers, load balancers, or OS patching. You push code, App Engine builds a container (or uses a runtime sandbox), provisions instances, and handles autoscaling, versioning, and traffic splitting. It sits between Cloud Functions (single-function, event-driven) and Compute Engine/GKE (full control over infra) — App Engine is the right choice when you have a full application (multiple routes, background work, static assets) and want zero infrastructure management.

  • Standard environment — sandboxed runtimes (Node.js, Python, Go, Java, PHP, Ruby), scales to zero, fastest cold starts, free tier eligible

  • Flexible environment — runs any Docker container on managed Compute Engine VMs, supports any language/binary, no scale-to-zero, slower deploys

  • Service — a deployable unit (formerly "module"); one App Engine app can have multiple services (e.g. default, api, worker)

  • Version — each deploy creates a new immutable version under a service; traffic is split/routed between versions

app.yaml & Deploying

Every service is configured through an app.yaml at its root. It declares the runtime, scaling policy, environment variables, and URL handlers. Deploys are one gcloud command — App Engine builds the app, uploads it, creates a new version, and (by default) migrates all traffic to it.

# app.yaml — standard environment, Node.js service
runtime: nodejs20
service: default          # omit for the default service
instance_class: F2        # F1/F2/F4/F4_1G — CPU/memory tier for standard env

env_variables:
  NODE_ENV: "production"
  DATABASE_URL: "postgres://user:pass@/dbname?host=/cloudsql/PROJECT:REGION:INSTANCE"

automatic_scaling:
  min_instances: 0         # scale to zero when idle (standard env only)
  max_instances: 10
  target_cpu_utilization: 0.65
  max_concurrent_requests: 80

handlers:
  - url: /static
    static_dir: public/static
    secure: always          # force HTTPS

  - url: /.*
    script: auto            # route everything else to the app entrypoint

vpc_access_connector:
  name: projects/my-project/locations/us-central1/connectors/my-connector
# One-time: create the App Engine app in a region (irreversible choice!)
gcloud app create --region=us-central

# Deploy the default service from app.yaml in the current dir
gcloud app deploy

# Deploy without shifting traffic — good for canary testing
gcloud app deploy --no-promote --version=v2-canary

# Deploy a named service (service: api in api/app.yaml)
gcloud app deploy api/app.yaml

# View the deployed app
gcloud app browse

# Stream logs
gcloud app logs tail -s default

# List versions and traffic split
gcloud app versions list
gcloud app services set-traffic default --splits=v2-canary=10,v1=90

Traffic Splitting & Rollbacks

Because every deploy creates a new immutable version, canary releases and instant rollbacks are built in — no separate blue/green tooling required. Split traffic by IP hash (sticky sessions) or by random cookie, and roll back by re-pointing traffic at the previous version, which is still running and warm.

# Gradual rollout: 90% old, 10% new
gcloud app services set-traffic default --splits=v1=0.9,v2=0.1 --split-by=ip

# Once confident, shift 100% to the new version
gcloud app services set-traffic default --splits=v2=1

# Instant rollback — just re-point traffic at the previous stable version
gcloud app services set-traffic default --splits=v1=1

# Delete old versions once you're done (they keep costing money if min_instances > 0)
gcloud app versions delete v1 --service=default

Cron Jobs & Task Queues

App Engine ships with two built-in background primitives so you rarely need an external scheduler for simple periodic or async work inside the same project: cron.yaml for scheduled jobs, and Cloud Tasks for retryable async work queued from your request handlers.

# cron.yaml — deployed with `gcloud app deploy cron.yaml`
cron:
  - description: "nightly cleanup of expired sessions"
    url: /tasks/cleanup-sessions
    schedule: every 24 hours
    time_zone: America/New_York
    target: default          # which service handles the request

  - description: "sync billing every 15 minutes"
    url: /tasks/sync-billing
    schedule: every 15 minutes
// Enqueue an async task from a request handler (Node.js, @google-cloud/tasks)
const { CloudTasksClient } = require('@google-cloud/tasks')
const client = new CloudTasksClient()

async function enqueueWelcomeEmail(userId) {
  const parent = client.queuePath('my-project', 'us-central1', 'email-queue')
  const task = {
    appEngineHttpRequest: {
      httpMethod: 'POST',
      relativeUri: '/tasks/send-welcome-email',
      body: Buffer.from(JSON.stringify({ userId })).toString('base64'),
      headers: { 'Content-Type': 'application/json' },
    },
  }
  const [response] = await client.createTask({ parent, task })
  return response.name
}

// The App Engine handler that Cloud Tasks calls back into
app.post('/tasks/send-welcome-email', async (req, res) => {
  const { userId } = req.body
  await sendWelcomeEmail(userId)
  res.status(200).send('OK')   // 2xx = task succeeds; else Cloud Tasks retries with backoff
})

Pitfalls & Practical Tips

  • The region you pick with gcloud app create is permanent for the lifetime of the project — there is no migration path, only delete-and-recreate the whole app

  • min_instances: 0 in the standard environment means cold starts on the first request after idle — set min_instances: 1+ for latency-sensitive services, at the cost of always-on billing

  • Flexible environment instances never scale to zero and take minutes (not seconds) to deploy — don't use it for spiky, low-traffic workloads where standard would scale to zero and save money

  • Connect to Cloud SQL via the Unix socket path (/cloudsql/PROJECT:REGION:INSTANCE), not a public IP — App Engine standard has no VPC access by default without a Serverless VPC Access connector

  • Old versions keep running (and can keep billing if min_instances > 0) until you explicitly delete them — clean up unused versions regularly

  • Each service+version combo gets its own URL (VERSION-dot-SERVICE-dot-PROJECT_ID.REGION.r.appspot.com) — useful for testing a specific version directly without shifting the default route

Keep your App Engine knowledge sharp.

Save this stack to your personal DevRecall — add your own notes, track what you're learning, and share what you know with the community.

Get started — free forever