Process Orchestration

Process Orchestration is pyserve's flagship feature for running multiple Python web applications with full process isolation. Each application runs in its own subprocess with independent lifecycle, health monitoring, and automatic restart on failure.

Overview

Unlike ASGI Mounting which runs apps in-process, Process Orchestration provides:

Architecture

                    PyServe Gateway (:8000)
                           │
          ┌────────────────┼────────────────┐
          ▼                ▼                ▼
      FastAPI          Flask           Starlette
       :9001           :9002             :9003
      /api/*          /admin/*           /ws/*
        

PyServe acts as a gateway, routing requests to the appropriate subprocess based on URL path.

Basic Configuration

server:
  host: 0.0.0.0
  port: 8000

extensions:
  - type: process_orchestration
    config:
      apps:
        - name: api
          path: /api
          app_path: myapp.api:app
          
        - name: admin
          path: /admin
          app_path: myapp.admin:app

App Configuration Options

name
Unique identifier for the application (required)
path
URL path prefix for routing requests (required)
app_path
Python import path. Format: module:attribute (required)
app_type
Application type: asgi or wsgi. Default: asgi
workers
Number of uvicorn workers. Default: 1
port
Fixed port number. Default: auto-allocated from port_range
factory
If true, app_path points to a factory function. Default: false
env
Environment variables to pass to the subprocess
module_path
Path to add to sys.path for module resolution

Health Check Options

health_check_enabled
Enable health monitoring. Default: true
health_check_path
Endpoint to check for health. Default: /health
health_check_interval
Interval between health checks in seconds. Default: 10.0
health_check_timeout
Timeout for health check requests. Default: 5.0
health_check_retries
Failed checks before restart. Default: 3

Restart Options

max_restart_count
Maximum restart attempts before giving up. Default: 5
restart_delay
Initial delay between restarts in seconds. Default: 1.0
shutdown_timeout
Timeout for graceful shutdown. Default: 30.0

Global Configuration

extensions:
  - type: process_orchestration
    config:
      port_range: [9000, 9999]
      health_check_enabled: true
      proxy_timeout: 60.0
      logging:
        httpx_level: warning
        proxy_logs: true
        health_check_logs: false
      apps:
        # ...
port_range
Range for dynamic port allocation. Default: [9000, 9999]
proxy_timeout
Timeout for proxied requests in seconds. Default: 60.0
logging.httpx_level
Log level for HTTP client (debug/info/warning/error). Default: warning
logging.proxy_logs
Log proxied requests with latency. Default: true
logging.health_check_logs
Log health check results. Default: false

FastAPI Example

# myapp/api.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/health")
async def health():
    return {"status": "ok"}

@app.get("/users")
async def get_users():
    return [{"id": 1, "name": "Alice"}]
extensions:
  - type: process_orchestration
    config:
      apps:
        - name: api
          path: /api
          app_path: myapp.api:app
          workers: 4
          health_check_path: /health

Requests to /api/users are proxied to the FastAPI process as /users.

Flask Example (WSGI)

# myapp/admin.py
from flask import Flask

app = Flask(__name__)

@app.route("/health")
def health():
    return {"status": "ok"}

@app.route("/dashboard")
def dashboard():
    return {"page": "dashboard"}
extensions:
  - type: process_orchestration
    config:
      apps:
        - name: admin
          path: /admin
          app_path: myapp.admin:app
          app_type: wsgi
          workers: 2
Note: WSGI support requires a2wsgi package: pip install a2wsgi

Factory Pattern

# myapp/api.py
from fastapi import FastAPI

def create_app(debug: bool = False) -> FastAPI:
    app = FastAPI(debug=debug)
    
    @app.get("/health")
    async def health():
        return {"status": "ok", "debug": debug}
    
    return app
apps:
  - name: api
    path: /api
    app_path: myapp.api:create_app
    factory: true

Environment Variables

Pass environment variables to subprocesses:

apps:
  - name: api
    path: /api
    app_path: myapp.api:app
    env:
      DATABASE_URL: "postgresql://localhost/mydb"
      REDIS_URL: "redis://localhost:6379"
      DEBUG: "false"

Multiple Applications

extensions:
  - type: process_orchestration
    config:
      port_range: [9000, 9999]
      apps:
        # FastAPI REST API
        - name: api
          path: /api
          app_path: apps.api:app
          workers: 4
        
        # Flask Admin Panel
        - name: admin
          path: /admin
          app_path: apps.admin:app
          app_type: wsgi
          workers: 2
        
        # Starlette WebSocket Handler
        - name: websocket
          path: /ws
          app_path: apps.websocket:app
          workers: 1

Request Tracing

PyServe automatically generates and propagates X-Request-ID headers:

Process Orchestration vs ASGI Mount

Feature Process Orchestration ASGI Mount
Isolation Full process isolation Shared process
Memory Separate per app Shared
Crash Impact Only that app restarts All apps affected
Health Checks Yes, with auto-restart No
Multi-worker Yes, per app No
Latency HTTP proxy overhead In-process (faster)
Use Case Production, isolation needed Development, simple setups
When to use Process Orchestration:
  • Running multiple apps that shouldn't affect each other
  • Need automatic restart on failure
  • Different resource requirements per app
  • Production deployments
When to use ASGI Mount:
  • Development and testing
  • Simple setups with trusted apps
  • Minimal latency requirements
See Also: