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=90Traffic 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=defaultCron 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 createis permanent for the lifetime of the project — there is no migration path, only delete-and-recreate the whole appmin_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 connectorOld 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