How to deploy Ruvon¶
This guide covers deploying Ruvon workflows to production environments.
Overview¶
Ruvon supports multiple deployment models:
- Embedded SDK - Run in your existing Python application
- Standalone service - Deploy as FastAPI application
- Distributed workers - Scale with Celery workers
- Edge devices - Deploy to IoT/POS terminals
Production configuration¶
Environment variables¶
Set these for production deployments:
# Database
export DATABASE_URL="postgresql://user:password@host:5432/ruvon_prod"
export POSTGRES_POOL_MIN_SIZE=20
export POSTGRES_POOL_MAX_SIZE=100
# Celery (if using distributed execution)
export CELERY_BROKER_URL="redis://redis:6379/0"
export CELERY_RESULT_BACKEND="redis://redis:6379/0"
# Performance optimizations
export RUVON_USE_UVLOOP=true
export RUVON_USE_ORJSON=true
# Application settings
export ENVIRONMENT=production
export LOG_LEVEL=INFO
Database setup¶
Initialize PostgreSQL schema:
# Apply migrations
cd src/ruvon
export DATABASE_URL="postgresql://user:password@host:5432/ruvon_prod"
alembic upgrade head
# Verify migration status
alembic current
Deployment models¶
Model 1: Embedded SDK¶
Embed Ruvon in your existing application:
# app.py
import asyncio
from fastapi import FastAPI
from ruvon.builder import WorkflowBuilder
from ruvon.implementations.persistence.postgres import PostgresPersistenceProvider
from ruvon.implementations.execution.thread_pool import ThreadPoolExecutionProvider
app = FastAPI()
# Initialize Ruvon
persistence = None
builder = None
@app.on_event("startup")
async def startup():
global persistence, builder
# Initialize providers
persistence = PostgresPersistenceProvider(
db_url="postgresql://user:password@host:5432/ruvon_prod",
pool_min_size=20,
pool_max_size=100
)
await persistence.initialize()
execution = ThreadPoolExecutionProvider(max_workers=20)
builder = WorkflowBuilder(
config_dir="config/",
persistence_provider=persistence,
execution_provider=execution
)
@app.on_event("shutdown")
async def shutdown():
if persistence:
await persistence.close()
@app.post("/orders")
async def create_order(order_data: dict):
"""Create order and start workflow."""
# Start workflow
workflow = await builder.create_workflow(
workflow_type="OrderProcessing",
initial_data=order_data
)
return {
"order_id": order_data["order_id"],
"workflow_id": str(workflow.id)
}
Run with uvicorn:
Model 2: Standalone FastAPI service¶
Deploy Ruvon server separately:
Use REST API from your application:
import httpx
# Create workflow via API
async with httpx.AsyncClient() as client:
response = await client.post(
"http://ruvon-server:8000/workflows/start",
json={
"workflow_type": "OrderProcessing",
"initial_data": order_data
}
)
workflow_id = response.json()["workflow_id"]
Model 3: Distributed with Celery¶
Scale horizontally with Celery workers:
Start Celery workers:
# Worker 1
export DATABASE_URL="postgresql://..."
export CELERY_BROKER_URL="redis://..."
export CELERY_RESULT_BACKEND="redis://..."
celery -A ruvon.celery_app worker \
--loglevel=info \
--concurrency=10 \
--hostname=worker1@%h
# Worker 2
celery -A ruvon.celery_app worker \
--loglevel=info \
--concurrency=10 \
--hostname=worker2@%h
Start API server:
Docker deployment¶
Single container¶
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY src/ src/
COPY config/ config/
# Apply migrations on startup
COPY docker-entrypoint.sh /
RUN chmod +x /docker-entrypoint.sh
EXPOSE 8000
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["uvicorn", "ruvon_server.main:app", "--host", "0.0.0.0", "--port", "8000"]
# docker-entrypoint.sh
#!/bin/bash
set -e
# Apply migrations
cd /app/src/ruvon
alembic upgrade head
# Start application
cd /app
exec "$@"
Build and run:
docker build -t ruvon:latest .
docker run -d \
--name ruvon-server \
-p 8000:8000 \
-e DATABASE_URL="postgresql://..." \
ruvon:latest
Docker Compose¶
# docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:15
environment:
POSTGRES_USER: ruvon
POSTGRES_PASSWORD: ruvon_secret_2024
POSTGRES_DB: ruvon_prod
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ruvon"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
ruvon-server:
build: .
ports:
- "8000:8000"
environment:
DATABASE_URL: postgresql://ruvon:ruvon_secret_2024@postgres:5432/ruvon_prod
CELERY_BROKER_URL: redis://redis:6379/0
CELERY_RESULT_BACKEND: redis://redis:6379/0
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
celery-worker:
build: .
command: celery -A ruvon.celery_app worker --loglevel=info --concurrency=10
environment:
DATABASE_URL: postgresql://ruvon:ruvon_secret_2024@postgres:5432/ruvon_prod
CELERY_BROKER_URL: redis://redis:6379/0
CELERY_RESULT_BACKEND: redis://redis:6379/0
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
deploy:
replicas: 3 # Scale workers
volumes:
postgres_data:
Deploy:
Kubernetes deployment¶
Deployment manifests¶
# kubernetes/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ruvon-server
spec:
replicas: 3
selector:
matchLabels:
app: ruvon-server
template:
metadata:
labels:
app: ruvon-server
spec:
containers:
- name: ruvon
image: ruvon:latest
ports:
- containerPort: 8000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: ruvon-secrets
key: database-url
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 5
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: celery-worker
spec:
replicas: 5
selector:
matchLabels:
app: celery-worker
template:
metadata:
labels:
app: celery-worker
spec:
containers:
- name: celery
image: ruvon:latest
command: ["celery"]
args:
- "-A"
- "ruvon.celery_app"
- "worker"
- "--loglevel=info"
- "--concurrency=10"
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: ruvon-secrets
key: database-url
- name: CELERY_BROKER_URL
valueFrom:
secretKeyRef:
name: ruvon-secrets
key: celery-broker-url
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: ruvon-server
spec:
selector:
app: ruvon-server
ports:
- port: 80
targetPort: 8000
type: LoadBalancer
# kubernetes/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: ruvon-secrets
type: Opaque
stringData:
database-url: postgresql://user:password@postgres:5432/ruvon_prod
celery-broker-url: redis://redis:6379/0
celery-result-backend: redis://redis:6379/0
Deploy to Kubernetes:
kubectl apply -f kubernetes/secrets.yaml
kubectl apply -f kubernetes/deployment.yaml
kubectl get pods
kubectl logs -f deployment/ruvon-server
Horizontal Pod Autoscaler¶
# kubernetes/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: ruvon-server-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: ruvon-server
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: celery-worker-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: celery-worker
minReplicas: 5
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 80
Production checklist¶
Before deployment¶
- Database migrations applied (
alembic upgrade head) - Environment variables configured
- Secrets stored securely (not in code)
- PostgreSQL connection pool sized appropriately
- Celery workers configured (if using distributed execution)
- Health check endpoints working
- Logging configured
- Monitoring/metrics setup
Database¶
- PostgreSQL 13+ installed
- Connection pooling configured (min: 20, max: 100 for production)
- Backups configured
- SSL/TLS enabled for connections
- Read replicas setup (for read-heavy workloads)
Security¶
- Database credentials in environment variables or secrets manager
- API authentication enabled (if exposing REST API)
- Network security groups configured
- SSL/TLS certificates installed
- Rate limiting enabled
Monitoring¶
- Application logs forwarded to centralized logging
- Metrics exported (Prometheus/Datadog/CloudWatch)
- Alerts configured for failures
- Performance dashboards created
High availability¶
- Multiple server replicas (3+ recommended)
- Load balancer configured
- Database replication setup
- Redis cluster (if using Celery)
- Automated failover configured
Monitoring and observability¶
Health check endpoint¶
# In your FastAPI app
@app.get("/health")
async def health_check():
"""Health check endpoint for load balancer."""
try:
# Check database connection
await persistence.list_workflows(limit=1)
return {"status": "healthy"}
except Exception as e:
return {"status": "unhealthy", "error": str(e)}
Prometheus metrics¶
from prometheus_client import Counter, Histogram, Gauge
# Define metrics
workflows_started = Counter(
'workflows_started_total',
'Total workflows started',
['workflow_type']
)
workflow_duration = Histogram(
'workflow_duration_seconds',
'Workflow execution duration',
['workflow_type']
)
active_workflows = Gauge(
'active_workflows',
'Number of active workflows'
)
# Export endpoint
from prometheus_client import generate_latest
@app.get("/metrics")
async def metrics():
return Response(
generate_latest(),
media_type="text/plain"
)
Logging¶
Configure structured logging:
import logging
import json
class JSONFormatter(logging.Formatter):
def format(self, record):
log_obj = {
"timestamp": self.formatTime(record),
"level": record.levelname,
"message": record.getMessage(),
"logger": record.name
}
return json.dumps(log_obj)
# Configure logging
handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
logger = logging.getLogger("ruvon")
logger.addHandler(handler)
logger.setLevel(logging.INFO)
Scaling strategies¶
Vertical scaling¶
Increase resources per instance:
- Increase PostgreSQL connection pool size
- Increase Celery worker concurrency
- Increase server memory/CPU
Horizontal scaling¶
Add more instances:
- Deploy multiple FastAPI server replicas
- Deploy multiple Celery workers
- Use database read replicas for read-heavy workloads
Database scaling¶
- Enable connection pooling (pgBouncer)
- Setup read replicas
- Partition large tables
- Add database indexes
Troubleshooting production issues¶
Database connection errors¶
# Check connection pool exhaustion
kubectl logs deployment/ruvon-server | grep "pool exhausted"
# Increase pool size
export POSTGRES_POOL_MAX_SIZE=200
High memory usage¶
# Check for memory leaks
kubectl top pods
# Restart pods
kubectl rollout restart deployment/ruvon-server
Slow workflow execution¶
# Check database performance
# Query slow queries log
# Check Celery queue backlog
celery -A ruvon.celery_app inspect active
Next steps¶
See also¶
- Configuration guide
- Troubleshooting guide
- CLAUDE.md for migration and database setup
- Docker Compose files in
docker/directory