# App Engine standard - the PRODUCTION service.
#
# No password is committed here. DB_PASS and SECRET_KEY come from
# env_secrets.yaml, which is gitignored and rendered at deploy time from CI
# secrets (see env_secrets.yaml.example for the shape).
runtime: python310
service: default
# F1 is 600 MHz / 384 MiB and is the cheapest class. Measure before moving up:
# a faster core relieves nothing if the app is waiting on the database rather
# than burning CPU, and raising max_instances is the cheaper lever because it is
# billed only when the instances actually run.
instance_class: F1
automatic_scaling:
# Keep one instance warm so the app never cold-starts. Without this, App Engine
# scales to zero when idle and the next request pays a cold start; the worst
# case is a user returning from an external redirect (a payment provider, an
# OAuth consent screen) hitting the instance mid-boot and getting an "upstream
# connect error" from the load balancer.
min_instances: 1
# This number, WEB_THREADS, WEB_CONCURRENCY and the pool size in
# utils/db_wrapper.py are ONE decision:
#
# (pool_size + max_overflow) x WEB_CONCURRENCY x max_instances
#
# must fit inside the Cloud SQL instance's max_connections (100 by default),
# minus whatever else shares that instance. At the defaults this is
# (4+1) x 2 x 4 = 40. Raising this alone is what exhausts the database.
max_instances: 4
entrypoint: gunicorn -c gunicorn.py main:app
includes:
- env_secrets.yaml
# Mounts /cloudsql/<connection_name>. The deploy service account needs
# roles/cloudsql.client on the instance.
beta_settings:
cloud_sql_instances: myapp-000000:us-central1:myapp-postgres
env_variables:
# "prod" means "deployed behind HTTPS", not "the production environment": it is
# what sets SESSION_COOKIE_SECURE and arms the weak-SECRET_KEY guard in main.py.
ENV: prod
DB_USER: myapp
DB_NAME: myapp
INSTANCE_UNIX_SOCKET: myapp-000000:us-central1:myapp-postgres
# The canonical public origin. Security-sensitive external links (password
# reset, email verification, a payment provider's return URL) are built from
# it, so they point here rather than at whatever host a request arrived on.
PUBLIC_BASE_URL: https://myapp.example.com
# Outbound email. Non-secret config lives here; the mailbox password is
# SMTP_PASS in env_secrets.yaml. Delete these four lines to deploy with mail
# logged instead of sent.
# SMTP_HOST: mail.example.com
# SMTP_PORT: "587"
# SMTP_USER: noreply@myapp.example.com
# SMTP_FROM: noreply@myapp.example.com
handlers:
- url: /static
static_dir: static
- url: /.*
secure: always
redirect_http_response_code: 301
script: auto