Skip to content

Production Deployment

This guide covers deploying the Protean server in production — process management, containerization, scaling strategies, and health checks.

For basic server usage, see Run the Server. For the full production checklist — pool sizing, DLQ maintenance, subscription profiles, OTEL metrics, and graceful shutdown — see Harden the Server.

Process Management

Use a process manager like systemd, supervisord, or Docker. Send SIGTERM to trigger graceful shutdown and give the process at least 15 seconds to drain in-flight handlers:

# /etc/systemd/system/protean-server.service
[Unit]
Description=Protean Message Server
After=network.target

[Service]
Type=simple
User=app
WorkingDirectory=/app
Environment=PROTEAN_ENV=production
ExecStart=/app/.venv/bin/protean server --domain=my_domain
Restart=always
RestartSec=5
KillSignal=SIGTERM
TimeoutStopSec=30

[Install]
WantedBy=multi-user.target

TimeoutStopSec=30 gives the engine up to 30 seconds to stop subscriptions, drain in-flight handlers (bounded at 10s), and close providers, brokers, caches, and the event store before systemd escalates to SIGKILL. See Shut down gracefully.

Docker

Expose port 8080 so the orchestrator can reach the built-in health server:

FROM python:3.11-slim

COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

WORKDIR /app
COPY . .
RUN uv sync

ENV PROTEAN_ENV=production
EXPOSE 8080
CMD ["uv", "run", "protean", "server", "--domain=my_domain"]

Docker sends SIGTERM by default and waits for --stop-timeout (default 10s). Raise it for heavier workloads:

docker run --stop-timeout 30 my-app:latest

Kubernetes

The engine embeds a health server on port 8080 by default. Wire livenessProbe and readinessProbe to /livez and /readyz, and set terminationGracePeriodSeconds long enough for the shutdown sequence to complete:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: protean-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: protean-server
  template:
    metadata:
      labels:
        app: protean-server
    spec:
      terminationGracePeriodSeconds: 30
      containers:
      - name: server
        image: my-app:latest
        command: ["protean", "server", "--domain=my_domain"]
        env:
        - name: PROTEAN_ENV
          value: "production"
        ports:
        - name: health
          containerPort: 8080
        livenessProbe:
          httpGet: { path: /livez, port: health }
          periodSeconds: 10
          failureThreshold: 3
        readinessProbe:
          httpGet: { path: /readyz, port: health }
          periodSeconds: 5
          failureThreshold: 2
        lifecycle:
          preStop:
            exec:
              command: ["sleep", "5"]
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"

The preStop hook gives the service mesh or load balancer a moment to drain connections before the engine starts shutting down. For the full probe reference — response bodies, status codes, and how to move the port — see Server Hardening reference.

FastAPI apps

API pods serving HTTP traffic should mount the equivalent router on their FastAPI app:

from fastapi import FastAPI
from protean.integrations.fastapi.health import create_health_router

app = FastAPI()
app.include_router(create_health_router(domain))

Point the probes at the same ports your API already exposes — no separate health server is needed.

Scaling Considerations

StreamSubscription supports horizontal scaling:

  • Multiple server instances can run concurrently
  • Messages are distributed across consumers via Redis consumer groups
  • Each message is processed by exactly one consumer

EventStoreSubscription has limited scaling:

  • Multiple instances will process the same messages
  • Use for projections where idempotency is guaranteed
  • Consider using StreamSubscription for scalable workloads

For connection pool sizing across workers, DLQ retention, and OTEL metric emission, follow the full production checklist in Harden the Server.