ESC

Type to search...

Production

Deploy, monitor, and scale Sandy’s franchise.

Sandy has 4 locations, 100+ orders per hour, and no visibility into what’s happening. Which location is slowest? Which orders are failing? She needs a dashboard.

evo install dashboard
from evoid_dashboard import create_dashboard

# Monitoring UI at http://localhost:8001
create_dashboard(port=8001)

The dashboard shows: service map, all registered Intents, message bus history, database connections, system info. Sandy can see every location’s health in one place.

Production Config

# evoid.toml
[project]
name = "sandy-franchise"
version = "1.0.0"

[runtime]
adapter = "asgi"
host = "0.0.0.0"
port = 8000

[engines]
schema = "native"
storage = "sqlite"
cache = "memory"
logger = "loguru"

[pipeline]
timeout = 10.0

Running in Production

# With uvicorn
uvicorn my_app:app --host 0.0.0.0 --port 8000 --workers 4

# Or with evo CLI
evo service run sandy-franchise --host 0.0.0.0 --port 8000

Strict Mode

Enable strict mode to catch missing processors before they silently fail in production:

from evoid.core.runtime import Config

config = Config(
    name="sandy-prod",
    strict=True,  # Raises LookupError if a processor is not registered
)

Without strict mode, missing processors are silently skipped. In production, you want to know immediately.

Docker

FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install uv && uv sync
EXPOSE 8000
CMD ["evo", "service", "run", "sandy-franchise"]

Monitoring

from evoid.core.pipeline import Result

async def monitor(intent: Intent) -> dict:
    """Track pipeline performance."""
    result = await execute(intent)

    # Log metrics
    print(f"Intent: {intent.name}")
    print(f"Duration: {result.duration:.3f}s")
    print(f"Processors: {len(result.processors)}")
    print(f"Success: {result.success}")

    return result.value

Scaling

StrategyWhen
Multiple workersCPU-bound, single location
Multiple servicesDifferent domains (orders, inventory)
Message BusCross-service communication
Parallel executionBatch processing

Health Checks

Add a health endpoint for load balancers and monitoring:

from evoid.adapters.asgi import get
from evoid.web.route import Service

app = Service("sandy-api")

@get("/health", level="ephemeral")
async def health() -> dict:
    return {"status": "healthy", "version": "1.0.0"}

@get("/ready", level="ephemeral")
async def readiness() -> dict:
    # Check database connection
    try:
        await db.execute("SELECT 1")
        return {"status": "ready", "database": "ok"}
    except Exception as e:
        return {"status": "not_ready", "database": str(e)}, 503

Environment Config

Different configs for dev, staging, production:

# config/development.py
from evoid.core.runtime import Config

config = Config(
    name="sandy-dev",
    adapter="asgi",
    engines={"storage": "memory", "cache": "memory"},
)

# config/production.py
config = Config(
    name="sandy-prod",
    adapter="asgi",
    engines={"storage": "sqlite", "cache": "memory"},
)

Graceful Shutdown

Handle shutdown signals cleanly:

import signal
import asyncio

shutdown_event = asyncio.Event()

def handle_shutdown(sig, frame):
    print("Shutting down...")
    shutdown_event.set()

signal.signal(signal.SIGTERM, handle_shutdown)
signal.signal(signal.SIGINT, handle_shutdown)

# Wait for shutdown signal
await shutdown_event.wait()

# Cleanup: close DB connections, flush caches, etc.
await db.close()
cache.flush()

What You Learned

ConceptWhat It Is
Production configSettings for real deployment
uvicorn / DockerRunning in production
MonitoringTrack performance and errors
Scaling strategiesWorkers, services, parallelism

Next: What’s Next

Let’s recap Sandy’s journey — What’s Next.