A practical walkthrough for creating and deploying multiple App Engine services with request routing.


Multi-Service Architecture

Each App Engine service is an independent component with its own code, configuration, and runtime. You deploy each service separately, and they communicate via HTTP or dispatch.yaml routing rules.

my-project/
├── default-service/
│   ├── app.yaml          # service: default (or omit)
│   ├── main.py
│   └── requirements.txt
├── api-service/
│   ├── app.yaml          # service: api
│   ├── main.py
│   └── requirements.txt
├── worker-service/
│   ├── app.yaml          # service: worker
│   ├── main.py
│   └── requirements.txt
└── dispatch.yaml         # routing rules (project root)

Key Insight: Each service lives in its own directory with its own app.yaml. The service field in app.yaml defines the service name. The first service deployed becomes the default service.


Step-by-Step Example

This example creates three services: a web frontend (default), a REST API (api), and a background worker (worker).

1. Default Service (Web Frontend)

default-service/app.yaml:

runtime: python312
# service: default  -- optional, first deploy is automatically "default"
 
instance_class: F2
 
automatic_scaling:
  min_instances: 0
  max_instances: 10
  target_cpu_utilization: 0.65
 
handlers:
  - url: /static
    static_dir: static
  - url: /.*
    script: auto
    secure: always

default-service/main.py:

from flask import Flask
 
app = Flask(__name__)
 
@app.route("/")
def home():
    return "Welcome to the web frontend!"
 
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

default-service/requirements.txt:

Flask==3.0.0
gunicorn==21.2.0

2. API Service

api-service/app.yaml:

runtime: python312
service: api
 
instance_class: F2
 
automatic_scaling:
  min_instances: 1
  max_instances: 5
  target_cpu_utilization: 0.65

api-service/main.py:

from flask import Flask, jsonify
 
app = Flask(__name__)
 
@app.route("/api/status")
def status():
    return jsonify({"status": "healthy", "service": "api"})
 
@app.route("/api/data")
def data():
    return jsonify({"items": ["a", "b", "c"]})
 
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

3. Worker Service

worker-service/app.yaml:

runtime: python312
service: worker
 
instance_class: B2
 
basic_scaling:
  max_instances: 3
  idle_timeout: 10m

worker-service/main.py:

from flask import Flask, request
 
app = Flask(__name__)
 
@app.route("/tasks/process")
def process():
    task_id = request.args.get("id", "unknown")
    return f"Processing task {task_id}"
 
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

4. Dispatch Rules

dispatch.yaml (at project root):

dispatch:
  # Route API requests to the api service
  - url: "*/api/*"
    service: api
 
  # Route task processing to the worker service
  - url: "*/tasks/*"
    service: worker
 
  # Everything else goes to the default service
  - url: "*/*"
    service: default

Rules are evaluated in top-to-bottom order. First match wins.


Deployment

Deploy each service individually, then deploy the dispatch rules:

# Deploy the default service first (required)
cd default-service
gcloud app deploy app.yaml
 
# Deploy the API service
cd ../api-service
gcloud app deploy app.yaml
 
# Deploy the worker service
cd ../worker-service
gcloud app deploy app.yaml
 
# Deploy dispatch rules
cd ..
gcloud app deploy dispatch.yaml

Note: The default service must be deployed first. You cannot deploy other services until the default service exists.


Accessing Your Services

After deployment, each service is accessible via its own URL:

ServiceURL PatternExample
defaulthttps://PROJECT_ID.REGION_ID.r.appspot.comhttps://my-project.uc.r.appspot.com
apihttps://api-dot-PROJECT_ID.REGION_ID.r.appspot.comhttps://api-dot-my-project.uc.r.appspot.com
workerhttps://worker-dot-PROJECT_ID.REGION_ID.r.appspot.comhttps://worker-dot-my-project.uc.r.appspot.com

With dispatch rules, requests are automatically routed:

  • https://my-project.uc.r.appspot.com/default service
  • https://my-project.uc.r.appspot.com/api/statusapi service
  • https://my-project.uc.r.appspot.com/tasks/processworker service

dispatch.yaml Rules

Syntax

dispatch:
  - url: "PATTERN"
    service: SERVICE_NAME

URL pattern options

PatternMatchesExample
"*/api/*"Any host, /api/ path prefixRoutes all API calls
"example.com/*"Specific domain onlyCustom domain routing
"*/*"EverythingCatch-all for default service

Rules and limits

RuleDetail
Evaluation orderTop-to-bottom, first match wins
Maximum rules20 per application
Wildcards* matches any host or path segment
Deploy separatelydispatch.yaml is deployed independently from service code
LocationMust be in the root directory or alongside the default service

Warning: If no dispatch rule matches a request, it routes to the default service. Always include a catch-all rule ("*/*") at the end of your dispatch.yaml.


Key Constraints

ConstraintDetail
Default service requiredEvery app must have a default service, deployed first
Default cannot be deletedOnly non-default services can be removed
Service name limits1-63 characters, lowercase letters, digits, hyphens
Maximum services20 per application
Independent configurationEach service has its own runtime, scaling, and instance class
Mixed environmentsServices can use different environments (Standard/Flexible)

Common Patterns

PatternServicesUse Case
Frontend + APIdefault (Standard) + api (Standard)Typical web application
Web + Workerdefault (Standard) + worker (Basic/Manual)Background task processing
Multi-runtimedefault (Python) + api (Node.js)Polyglot microservices
Mixed environmentdefault (Standard) + processing (Flexible)Standard for web, Flexible for heavy compute

TL;DR

  • Each service gets its own directory with its own app.yaml.
  • The service field in app.yaml defines the service name (omit for default).
  • Deploy the default service first — it’s required before any other service.
  • Use dispatch.yaml to route requests between services based on URL patterns.
  • Rules are evaluated top-to-bottom, first match wins.
  • Services can use different runtimes, environments, and scaling configurations.
  • Service limits depend on billing: 5 services for free apps, 210 services for paid apps.

Resources

Creating Multiple Services Official guide to multi-service architecture.

dispatch.yaml Reference Complete reference for dispatch routing rules.

How Requests are Routed Request routing mechanisms and URL formats.

app.yaml Configuration Full reference for the app.yaml configuration file.

Core Components Architecture overview including services and versions.