Configuration
Scale settings live in Settings → Scale and in environment variables. Connection changes take effect when the process restarts.
REDIS_URL and SCALE_POSTGRES_URL in the environment override the values saved in Settings → Scale, which is usually how a container deployment configures this. Several other settings are read only from the environment — each section below says which. The screen shows each of them read-only, with the value in effect on the main instance and the variable that sets it: change the variable and restart.
Scale mode turns on when Redis is set
There is no separate switch at the process level: an instance that starts with a Redis URL is in scale mode, one that starts without is standalone. Saving a new URL takes effect at the next restart.
The scale Postgres is the same database as SYNAPSE_DB_URL
scale_postgres_url predates the store and was kept under its old name so
existing deployments did not have to change. Left empty, the scale layer uses
SYNAPSE_DB_URL, which is what most deployments want. If you do set it, point
it at the same Postgres — two different databases means the workers and the
main instance disagree about what exists. See
Environment variables.
Redis connection
Redis URL
The connection string for your Redis instance. Test checks it without saving.
| Format | Example |
|---|---|
| Single node | redis://hostname:6379/0 |
| With password | redis://:password@hostname:6379/0 |
| TLS (rediss) | rediss://hostname:6380/0 |
| Redis Cluster | redis+cluster://node1:6379,node2:6379,node3:6379 |
The database index at the end (/0) is optional and defaults to 0. Use a dedicated database index if you share a Redis instance across applications.
A redis+cluster:// URL is detected automatically. The job queue itself is served from the first node in the list; the cluster client is used for event streams.
For managed Redis services, use the connection string provided by your provider. Most managed services (Upstash, Redis Cloud, ElastiCache) use rediss:// (with double-s) for TLS connections.
Queued jobs are keys in Redis. Don't run it with an eviction policy that can remove them, and turn on persistence (AOF) so a Redis restart keeps the queue — see Deployment → Production checklist.
Enable scale mode
This switch does not start or stop scale mode, which depends only on the Redis URL at startup. Its one effect today is to turn off local (stdio) MCP servers, which scale mode never runs anyway.
Worker concurrency
| Default | Range |
|---|---|
10 | 1 – 100 |
The number of jobs a worker process runs at once. Read only from the WORKER_CONCURRENCY environment variable, which each worker takes from its own environment — set the variable on each worker. The screen shows the main instance's value, read-only.
Workers use asyncio, so concurrent jobs run as coroutines, not threads:
- For I/O-heavy workloads (LLM API calls, web requests, database queries),
10–20per worker is a reasonable starting point. The worker spends most of its time waiting on external services, so concurrency translates directly to throughput. - For CPU-heavy workloads (local model inference, large data processing), keep concurrency lower (
3–5) and run more worker instances in parallel.
Different worker pools can run with different WORKER_CONCURRENCY values.
Postgres
The main instance and the workers use one Postgres database, which is SynapseOrch's store. There is nothing to sync: a workflow, agent, tool or MCP server saved in the UI is in the database the next job reads, and each job reads its tenant's settings — model keys included — at the start.
Postgres URL
Test checks the connection without saving.
| Format | Example |
|---|---|
| Standard | postgresql://user:password@host:5432/dbname |
| SSL required | postgresql://user:password@host:5432/dbname?sslmode=require |
| Via PgBouncer | postgresql://user:password@pgbouncer-host:6432/dbname |
Left empty, the scale layer uses SYNAPSE_DB_URL. SynapseOrch converts postgresql:// and postgres:// to the postgresql+asyncpg:// driver form for you, here, in SCALE_POSTGRES_URL and in SYNAPSE_DB_URL.
PgBouncer mode
Opens and closes a connection per query instead of keeping a pool, which PgBouncer in transaction pooling mode requires. SynapseOrch does not run PgBouncer; this is for one you put in front of Postgres yourself. It is read only from the PGBOUNCER_MODE=1 environment variable; Settings → Scale shows the main instance's value, read-only. Set the variable on the main instance and on every worker when their Postgres URL points at PgBouncer, and not otherwise — it adds a connection per query.
S3 storage
When a bucket is configured, it replaces SYNAPSE_BLOB_DIR as the blob store: the vault, run logs, notifications and cached responses are written to the bucket, in the main instance, in every worker, and in the tool processes they start. Keys are <prefix>/<tenant>/… — synapse/default/vault/notes.md on a single-tenant install with the default prefix. With one bucket shared by every process, a deployment across several hosts needs no shared directory. See Scale Mode → What you need.
The bucket is read when a process starts, from S3_BUCKET or else the saved setting, so a change takes a restart. Set the S3_* variables on every process — in .env for Docker Compose — so each one uses the same bucket. Nothing already in SYNAPSE_BLOB_DIR is copied to the bucket.
S3 bucket
The name of the bucket. It must already exist, and the credentials need s3:GetObject, s3:PutObject, s3:DeleteObject and s3:ListBucket. Test checks access.
Region
| Default |
|---|
us-east-1 |
The AWS region where the bucket is located.
Key prefix
| Default |
|---|
synapse |
All objects are stored under this prefix. Useful when sharing a bucket across deployments.
Access key ID and secret access key
IAM credentials for S3 access. Leave both blank to use the environment's AWS credentials instead — an instance profile, an ECS task role, EKS IRSA, or any other credential chain mechanism.
Endpoint URL
Leave blank for AWS S3. Set this to use an S3-compatible object store:
| Provider | Endpoint format |
|---|---|
| MinIO (self-hosted) | http://minio-host:9000 |
| Cloudflare R2 | https://{account_id}.r2.cloudflarestorage.com |
| Backblaze B2 | https://s3.{region}.backblazeb2.com |
| DigitalOcean Spaces | https://{region}.digitaloceanspaces.com |
Workers
Workers lists the workers registered in the workers table — id, status, active and maximum jobs, and health address — when the main instance is itself in scale mode. It does not refresh on its own; use the refresh button. Each worker can be health-checked, which calls its /health address, or removed from the list.
A worker registers itself when it starts and on each heartbeat; there is nothing to add by hand.
The Scale screen also shows analytics for recent runs, a lookup by run or session id, the Live queue, and Failed jobs (DLQ) with a retry for each. A retry, like Resume from failure on a run a worker ran, is queued for the workers rather than run inside the API process.
Observability
OTLP endpoint — or the OTLP_ENDPOINT variable — turns on OpenTelemetry export to your own collector, from the API and every worker: ids, timings, token counts and cost, never prompts, answers or tool data. It applies on restart and needs the telemetry packages, which the Docker images carry.
Metrics token is read only from METRICS_TOKEN. With it set, the API serves Prometheus metrics at GET /api/v2/metrics, with the token as a Bearer token. The screen shows whether one is set, never the token.
See Telemetry for what is exported and how to try it locally.
Max global queue depth
| Default |
|---|
1,000,000 |
When the queue holds this many jobs, new V2 run submissions return 503 Service Unavailable with Retry-After: 60. A backpressure valve against traffic spikes. Read only from MAX_GLOBAL_QUEUE_DEPTH.
Rate limit per tenant
| Default |
|---|
1,000 req/s |
Maximum V2 run submissions per second, per tenant. Above it, requests return 429 Too Many Requests. Read only from RATE_LIMIT_PER_TENANT_RPS.
A tenant also can't have more than 1,000 queued, running or paused runs at once; the next submission returns 429.
Environment variables reference
Pass these to the worker image with -e, --env-file, a Kubernetes Secret or ConfigMap, or a Compose environment: block. The connection, database and storage variables apply to the main instance and API replicas too.
Required
| Variable | Description |
|---|---|
REDIS_URL | Redis connection URL. Examples: redis://host:6379/0, redis+cluster://h1:6379,h2:6379 |
SYNAPSE_DB_URL | The database the main instance uses: postgresql://user:pass@host:5432/dbname, or the postgresql+asyncpg:// form. The worker image sets no default |
Optional
| Variable | Description |
|---|---|
SCALE_POSTGRES_URL | A different URL for the scale layer's connection to that same database. Unset, it uses SYNAPSE_DB_URL |
Worker tuning
| Variable | Default | Description |
|---|---|---|
WORKER_CONCURRENCY | 10 | Maximum concurrent jobs per worker process |
WORKER_JOB_TIMEOUT | 3600 | Seconds before a job is cancelled and failed. A killed worker's jobs become available again this long plus 10 seconds after they started |
WORKER_MAX_RETRIES | 3 | Tries a run gets when a stopping worker cuts it off; each resumes from its checkpoint. After the last, the run is failed and dead-lettered, and Retry resumes it. A job that raises an error is not retried |
WORKER_JOB_COMPLETION_WAIT | 45 | Seconds a stopping worker gives the jobs it is running to finish before cancelling them. Keep it under the orchestrator's grace period (stop_grace_period, terminationGracePeriodSeconds) |
WORKER_HEALTH_PORT | 9000 | Port for the HTTP health endpoint (GET /health) |
WORKER_ID | worker-<hostname>-<random> | The id the worker registers under |
SYNAPSE_MCP_POOL_SIZE | 32 | How many tenants' MCP server sets a worker keeps open |
SYNAPSE_MCP_IDLE_TTL | 900 | Seconds before an idle tenant's MCP servers are closed |
Database
| Variable | Default | Description |
|---|---|---|
PGBOUNCER_MODE | 0 | Set to 1 when the Postgres URL points to PgBouncer in transaction pooling mode |
Storage
| Variable | Default | Description |
|---|---|---|
SYNAPSE_BLOB_DIR | /blobs in the worker image and Docker Compose, /data/blobs in the single-container and API images | Where the vault, run logs and cached responses are written when no bucket is configured. Share it between the main instance and workers |
S3_BUCKET | (empty) | The bucket to use as the blob store instead of SYNAPSE_BLOB_DIR — see S3 storage |
S3_REGION | us-east-1 | AWS region of the bucket |
S3_PREFIX | synapse | Key prefix |
S3_ACCESS_KEY_ID | (empty) | IAM access key. Leave blank to use the environment's credentials |
S3_SECRET_ACCESS_KEY | (empty) | IAM secret key |
S3_ENDPOINT_URL | (empty) | Custom endpoint for S3-compatible stores (MinIO, Cloudflare R2, etc.) |
Events and limits
| Variable | Default | Description |
|---|---|---|
PUBSUB_EVENT_TTL | 3600 | Seconds to keep run events in Redis Streams. After this, events expire and cannot be replayed by reconnecting clients |
MAX_GLOBAL_QUEUE_DEPTH | 1000000 | Queue length at which V2 run submissions return 503 |
RATE_LIMIT_PER_TENANT_RPS | 1000 | V2 run submissions per second per tenant before 429 |
OTLP_ENDPOINT and METRICS_TOKEN are described under Observability. RUNS_RETENTION_DAYS and K8S_MODE are read but currently have no effect. NUM_QUEUE_SHARDS, WORKER_QUEUE_SHARD, DEFAULT_TENANT_ID and ENABLE_TENANT_ISOLATION are not read at all; the images no longer set or use them.