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
-
Deploy this repository using your host’s normal application workflow, or
build the
Dockerfileand setMODEtoweborgateway. -
Set
LLM_PROVIDERto your model provider (anthropic,openai,openrouter,trustedrouter,deepseek, orgemini). -
Set the corresponding API key:
ANTHROPIC_API_KEYforanthropicOPENAI_API_KEYforopenaiOPENROUTER_API_KEYforopenrouterTRUSTEDROUTER_API_KEYfortrustedrouterDEEPSEEK_API_KEYfordeepseekGEMINI_API_KEYforgemini
-
Add any integration or storage environment variables required by your
deployment, then verify health with
GET /healthorGET /ok.
.env.example and
Environment variables.
For hosted deployments that need persistent storage, set DATABASE_URI and
REDIS_URI. The async investigations API uses DATABASE_URL for its Postgres
store; see HTTP API.
Local gateway
Start a local HTTP server for investigations (and optional chat transports):0.0.0.0 using the PORT environment variable
(default 8000).
/investigate and /alerts accept only loopback callers. To allow
remote access, set OPENSRE_ALERT_LISTENER_TOKEN and send it as a bearer token:
Railway
- Provision Postgres and Redis in the Railway project.
- Set
DATABASE_URIandREDIS_URIon the OpenSRE service to those connection strings. - Optionally set
OPENSRE_DEPLOYMENT_METHOD=railwayfor telemetry labeling. - Deploy the service through your Railway project.
Horizontal scale-out
Run one gateway task and leaveOPENSRE_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:
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:
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.
Queued investigations (from POST /investigate) run in a separate background pool
that defaults to one at a time. Raise it to process more concurrently, still
bounded by the turn limit above:
Dedicated scheduler service
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=schedulertask. It runsopensre cron start --service, idling until tasks exist rather than exiting. - Set
OPENSRE_GATEWAY_HOST_SCHEDULER=0on the gateway tasks so they stop hosting the scheduler in-process.
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_URLabove, 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.