Skip to main content
OpenSRE deploys as a standard Python FastAPI application. Use the repository Dockerfile with Railway, EC2, ECS, Vercel, or another ASGI-capable host. For local containers, see Docker. The image supports three values of MODE:

Environment setup

  1. Deploy this repository using your host’s normal application workflow, or build the Dockerfile and set MODE to web or gateway.
  2. Set LLM_PROVIDER to your model provider (anthropic, openai, openrouter, trustedrouter, deepseek, or gemini).
  3. Set the corresponding API key:
    • ANTHROPIC_API_KEY for anthropic
    • OPENAI_API_KEY for openai
    • OPENROUTER_API_KEY for openrouter
    • TRUSTEDROUTER_API_KEY for trustedrouter
    • DEEPSEEK_API_KEY for deepseek
    • GEMINI_API_KEY for gemini
  4. Add any integration or storage environment variables required by your deployment, then verify health with GET /health or GET /ok.
Minimum configuration:
Provider keys and optional model overrides are listed in .env.example and Environment variables. For hosted deployments that need persistent storage, set DATABASE_URI and REDIS_URI. See HTTP API for the route reference.

Local gateway

Start a local HTTP server for health checks and alert intake (and optional chat transports):
The web app listens on 0.0.0.0 using the PORT environment variable (default 8000).
Example alert push:
By default, /alerts accepts only loopback callers. To allow remote access, set OPENSRE_ALERT_LISTENER_TOKEN and send it as a bearer token:
Complete route reference: HTTP API.

Railway

  1. Provision Postgres and Redis in the Railway project.
  2. Set DATABASE_URI and REDIS_URI on the OpenSRE service to those connection strings.
  3. Optionally set OPENSRE_DEPLOYMENT_METHOD=railway for telemetry labeling.
  4. Deploy the service through your Railway project.

Horizontal scale-out

Run one gateway task and leave OPENSRE_SESSION_FILE_LOCK off. More than one gateway task sharing OPENSRE_HOME is not supported yet. Follow #5474 and add a second task only after that lands. To serve more concurrent conversations today, raise concurrency on that single task (next section). After #5474, put every task on the same session store — a shared mount such as S3 Files or EFS — and set:
Slack Events API tasks must also share DATABASE_URL. Without it, a Slack retry that lands on another replica runs the same turn twice. Socket Mode, Telegram, and Discord do not use that store. Do not set SLACK_GATEWAY_ALLOW_LOCAL_DEDUP=1 on more than one replica. Do not turn the lock on to make a multi-task fleet safe until then. Even after the fix, fcntl.flock is weak on some shared mounts (NFS, and EFS depending on configuration) — stay on a single task there, or use a mount that honors POSIX locks.

Concurrency per task

Each task has two caps, and the lower one wins. OPENSRE_MAX_CONCURRENT_TURNS limits every turn in the process (default 1 on SMALL). Slack, Telegram, and Discord each have their own pool (SLACK_GATEWAY_MAX_CONCURRENT, TELEGRAM_GATEWAY_MAX_CONCURRENT, DISCORD_GATEWAY_MAX_CONCURRENT), which also defaults to 1 on SMALL. Raising only the process cap leaves chat at one concurrent turn. Turns are I/O-bound — mostly waiting on the model — so a small task can run several; the ceiling is the task’s memory, since each concurrent turn holds its context resident. Raise both without changing the task size, using the transport vars for the chats you run:
Or set OPENSRE_SIZE_PROFILE=MEDIUM (2) or LARGE (4) to raise both defaults together. Read the gateway_turn_memory debug lines (delta_mb, peak_mb) from a real run to size this against the task’s memory limit before raising it.

Dedicated scheduler service

Scheduled callbacks use a bounded worker pool. At most two distinct tasks run at once by default; set OPENSRE_SCHEDULER_MAX_CONCURRENT_RUNS to a positive integer to change that limit. A task never overlaps its own previous run. Runs waiting for a scheduler worker appear as pending in run history and resume after a scheduler restart. On restart, pending runs for paused tasks wait until the task is enabled again. Admitted runs wait until both a worker and the task are available. Cron ticks rejected because the same job is already queued or running are skipped. Invalid concurrency settings fall back to two workers. Agentic scheduled runs also take a permit from OPENSRE_MAX_CONCURRENT_TURNS, so the lower of the scheduler and process-wide limits bounds their effective concurrency; interactive traffic shares the process-wide limit. Recovery work uses the same scheduler worker pool. By default the gateway process also runs the cron/loop scheduler. With more than one gateway task that would fire every scheduled task once per task. Run the scheduler as its own single service instead:
  • Deploy one MODE=scheduler task. It runs opensre cron start --service, idling until tasks exist rather than exiting.
  • Set OPENSRE_GATEWAY_HOST_SCHEDULER=0 on the gateway tasks so they stop hosting the scheduler in-process.
Run exactly one scheduler service, never one per gateway task. The scheduler service and every process that mutates tasks — the gateway, opensre cron, and /loops — must share the same task store: the same OPENSRE_HOME on a shared mount (S3 Files / EFS). The scheduler reconciles from that file, so a separate filesystem would leave it running a stale task set.

Optional: conversation affinity

Skip affinity until you run more than one gateway task (after #5474). Then it is a performance option: it keeps a conversation’s warm agent in one task’s memory instead of cold-rebuilding when a turn lands elsewhere. Affinity is a routing concern, not an app setting:
  • Behind an HTTP load balancer (Slack Events API), set the shared DATABASE_URL above, then enable consistent hashing on the conversation (Slack channel and thread) so a thread sticks to one task. Hashing does not replace event dedup.
  • With the pull transports (Slack Socket Mode, Telegram, Discord), each task connects independently and the provider spreads events across tasks, so there is no built-in affinity. Affinity would need an external dispatcher and is worth adding only when warm-agent reuse measurably helps.