> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omnara.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration

> Environment-variable reference for every control-plane process

All configuration is via environment variables. Use `.env.example` in the repo as a starting point.

> [!NOTE]
>
> **Required** refers to production. Setting `OMNARA_ALLOW_INSECURE_DEV_DEFAULTS=1`
> fills the fallbacks marked *dev* below (local Compose services, console email,
> an insecure secret-encryption key). Never set it in production.

## Core

| Variable | Purpose | Default | Required |
| - | - | - | - |
| `OMNARA_PUBLIC_URL` | Public app origin used in emails, browser links, and OAuth callbacks. Origin only, no path; HTTPS outside localhost. Set identically on API, worker, and maintenance. | — | Yes |
| `OMNARA_PUBLIC_API_URL` | Public base URL of the versioned API as daemons and machines reach it, including the version path. Its origin also serves `/install/omnarad.sh`. Set it identically on API, worker, and maintenance when the API is served from a different host than `OMNARA_PUBLIC_URL`. HTTPS outside localhost. | `OMNARA_PUBLIC_URL` + `/api/v1` | No |
| `OMNARA_BILLING_URL` | External billing page linked from the dashboard; the UI appends `?org={org-id}`. HTTPS outside localhost, no query parameters. Leave unset to hide billing in the dashboard. | — | No |
| `OMNARA_API_ADDR` | Public listen address of `api` | `:8080` | No |
| `OMNARA_API_METRICS_ADDR` | `api` health/metrics listener | `:8081` | No |
| `OMNARA_WORKER_METRICS_ADDR` | `worker` health/metrics listener | `:8082` | No |
| `OMNARA_MAINTENANCE_METRICS_ADDR` | `maintenance` health/metrics listener | `:8083` | No |
| `OMNARA_LOG_LEVEL` | `debug`, `info`, `warn`, or `error` | `info` | No |
| `OMNARA_ALLOW_INSECURE_DEV_DEFAULTS` | `1` enables local-development fallbacks | unset | No |
| `OMNARA_WORKER_EVENT_WEBHOOK_CONCURRENCY` | Maximum event webhook deliveries in flight per worker process | `128` | No |
| `OMNARA_EVENT_WEBHOOK_PER_ORG_CONCURRENCY` | Soft cap on concurrent event webhook deliveries per org across workers; use the same value on every worker | `128` | No |
| `OMNARA_WORKER_CAPACITY` | Maximum agent turns executed concurrently by each worker process | `4` | No |
| `OMNARA_WORKER_CLAIM_CONCURRENCY` | Concurrent claim loops per worker process | `16` | No |
| `OMNARA_WORKER_INBOX_CAPACITY` | Maximum integration inbox receipts processed concurrently per worker process; integer from 1 through 100 | `4` | No |
| `OMNARA_WORKER_ASYNC_TOOL_CAPACITY` | Maximum runtime-owned asynchronous tool calls in flight per worker process | `32` | No |
| `OMNARA_WORKER_BACKGROUND_TOOL_CAPACITY` | Background tool concurrency and in-memory queue capacity per worker process | `8` | No |
| `OMNARA_MAINTENANCE_INTERVAL` | Maintenance loop interval (Go duration) | `1s` | No |

### Monitoring agent execution

Alert on these counters:

* `increase(omnara_maintenance_job_items_total{outcome="mismatch"}[1h]) > 0`: an agent's stored execution state disagrees with its history.
* `increase(omnara_worker_claim_deferrals_total[15m]) > 0`: a worker could not claim an agent and retried it a minute later. Worker error logs name the agent.

Each worker reports `omnara_worker_capacity` (configured `OMNARA_WORKER_CAPACITY`),
`omnara_worker_slots_used` (occupied slots, including ones reserved while claiming or
backing off after a failure), and `omnara_worker_executions_active` (claimed work currently
executing). Graph cluster utilization as
`100 * sum(omnara_worker_slots_used) / sum(omnara_worker_capacity)`.

Maintenance samples the agent backlog about every 15 seconds:
`omnara_agent_ready_backlog` counts due wakeups for non-archived agents without a runtime
lock, and `omnara_agent_oldest_ready_age_seconds` is how long the oldest one has been
claimable (zero when empty). A wakeup becomes claimable at its ready time or when its
agent's previous runtime lock was released, whichever is later. Both report `NaN` before
the first sample or after a failed one; check
`omnara_agent_backlog_sample_last_success_timestamp_seconds` for freshness. Use the maximum
across maintenance replicas rather than summing.

Alert on sustained `omnara_agent_oldest_ready_age_seconds` with a fresh sample. High
utilization alongside the backlog means workers are at capacity; low utilization points to
a claiming or scheduling problem instead.

### Integration inbox operation

Inbox concurrency is independent of agent-turn concurrency. All worker activity
shares the database pool; raising inbox capacity does not raise its connection
limit. Measure queue lag and database contention before increasing concurrency
or replica counts.

Workers sample `omnara_integration_inbox_oldest_ready_lag_seconds` about every 30 seconds,
including while every consumer is busy. This measures time since `next_attempt_at`
for the oldest due queued receipt, including inactive integrations awaiting recovery;
future retry delays and processing leases are excluded. Scrapes read cached
values and never query the inbox. The metric has no integration or project labels and
reports zero for an empty ready queue, or `NaN` before sampling or after a failed
sample. Check `omnara_integration_inbox_lag_sample_last_success_timestamp_seconds` for
freshness; it remains unchanged on failure. Each worker observes the same global
queue, so compare the maximum lag across workers rather than summing it.

When processing confirms that a provider receipt has a content-free plan with no
recipients, completion discards its raw message payload. The receipt and duplicate-detection
key remain until normal cleanup. Pending or failed receipts retain their payloads, as do
receipts with message content in their plan and scheduled receipts whose payload is
needed to validate retries. This does not filter events before durable intake.

Inbox retention runs concurrently with the other core maintenance tasks. Completed/failed receipts
become eligible for deletion seven days after finishing, measured by the database
clock. Locks or cleanup backlogs can delay deletion. Deleting the integration, project or
organization can purge receipts earlier; disconnecting an integration preserves normal
retention.
Failed receipts are terminal, release unfinished launch
reservations, and follow the same retention policy. Queued and processing
receipts are never removed by terminal retention. Deleting a receipt ends its
transport deduplication window; exceptionally late replays can be accepted again.
Accepted agent history is independent and remains intact. Newer messages may pass
an older receipt waiting to retry.

Size database capacity for retained receipts as well as peak processing. Retained
row counts grow with incoming event rate and retention time. Measure representative
routing, payloads and competing workloads before sizing the deployment.

## Failed integration events

Accepted events retry automatically up to eight attempts with exponential delay.
A terminal failure releases unfinished launch reservations. Already committed agents
and inputs remain intact. A new mention or message can retry transient failures, but
a removed subscription must be restored before that conversation can forward messages.

The worker makes a best-effort failure notice for unfinished human requests when
provider access still works. Empty or fully admitted plans do not produce an
error message. Scheduled failures remain visible in the schedule's latest run;
a known opening thread can also receive a notice. Crash recovery can miss notices
and unused-upload cleanup without blocking new work. Inspect `integration inbox admission
failed` logs using the receipt, integration and project IDs. See the
[inbox operation and retention](#integration-inbox-operation).

### Shared-webhook setup failures

Independent saved integrations can share a physical bot and webhook endpoint. Known
invalid signatures and revoked/missing setups are skipped, but unknown secret,
decryption/KMS or database errors still return non-2xx. Healthy siblings commit
their receipts before that response; redelivery deduplicates those receipts.

Find `provider event integration failed` logs and use `integration_id`, `project_id`, provider,
`setup_revision`, `stage` and `retryable` to identify the affected setup. Logs
include the error type, not raw error text, payloads or credentials. Restore a
shared database/KMS outage first. For a persistently corrupt setup, reconnect
that saved integration with valid credentials for the same provider identity, or
disconnect it until repaired so independent healthy integrations can continue. Do not
acknowledge unknown failures merely because another integration succeeded.

After repair, recover events that failed before intake persisted them. Use the
provider's redelivery where available; otherwise, ask the sender to resend the message
or action. See the [integration guides](/integrations/overview) for provider-specific
setup. Disconnecting preserves retained inbox diagnostics. Reconnect before sending
fresh requests; previously failed work remains terminal.

## Database and Redis

| Variable | Purpose | Default | Required |
| - | - | - | - |
| `OMNARA_DATABASE_URL` | Postgres connection string used by `migrate` and the three long-running control-plane services | *dev:* local Compose Postgres | Yes |
| `OMNARA_REDIS_URL` | Redis or Valkey URL | *dev:* `redis://127.0.0.1:6379/0` | Yes |
| `OMNARA_MIGRATIONS_DIR` | Directory containing schema migrations; used only by `cmd/migrate` | `migrations` | No |
| `OMNARA_MIGRATION_TIMEOUT` | Overall deadline for the one-shot migration process (Go duration) | `30m` | No |

PostgreSQL 18 or newer is required. API and maintenance pools default to a maximum
of 10 connections per process. Worker pools default to
`max(10, ceil(OMNARA_WORKER_CAPACITY / 8))` maximum connections (256 capacity gives
32 connections). All three default to a minimum of 1 connection, a 30-minute idle
timeout, a 30-minute connection lifetime, and five minutes of lifetime jitter.
Explicit `pool_*` settings in `OMNARA_DATABASE_URL` override these defaults. Budget
connections across all service replicas.

Direct PostgreSQL, RDS Proxy, and PgBouncer session pooling work with these
settings. PgBouncer transaction pooling requires `max_prepared_statements > 0`;
statement pooling is not supported.

The migrator defaults to a 30-second lock timeout and a 15-minute statement
timeout. Explicit values in `OMNARA_DATABASE_URL` take precedence.

## Web serving and proxy

| Variable | Purpose | Default | Required |
| - | - | - | - |
| `OMNARA_WEB_SERVING` | `embedded` serves a SPA compiled into `cmd/api`; `disabled` leaves web assets to a separate service. The official cluster API image contains no frontend assets and sets this to `disabled`; the cluster web image serves only static assets and does not proxy API traffic. | `disabled` | No |
| `OMNARA_TRUSTED_PROXY_CIDRS` | Comma-separated proxy/load-balancer hop CIDRs whose `X-Forwarded-For`/`X-Real-IP` are trusted for auth rate limiting. Only hops you control. | — | No |

## Email

Signup and password reset send email through `OMNARA_EMAIL_DRIVER`. In production, use `smtp` or `sendgrid` — or disable both flows with `OMNARA_AUTH_SIGNUP_ENABLED=0` and `OMNARA_AUTH_PASSWORD_RESET_ENABLED=0`.

| Variable | Purpose | Default | Required |
| - | - | - | - |
| `OMNARA_EMAIL_DRIVER` | `none`, `console` (dev only), `smtp`, or `sendgrid` | `none` (*dev:* `console`) | When signup or reset is enabled |
| `OMNARA_EMAIL_FROM` | Sender address | — | With `smtp` or `sendgrid` |
| `OMNARA_SMTP_ADDR` | SMTP server `host:port` | — | With `smtp` |
| `OMNARA_SMTP_USERNAME` / `OMNARA_SMTP_PASSWORD` | SMTP credentials; set both or neither | — | No |
| `OMNARA_SMTP_REQUIRE_TLS` | Require STARTTLS; disabling is dev-only | `1` | No |
| `SENDGRID_API_KEY` | SendGrid API key | — | With `sendgrid` |

## Auth and SSO connectors

| Variable | Purpose | Default | Required |
| - | - | - | - |
| `OMNARA_AUTH_SIGNUP_ENABLED` | First-party password signup | `1` | No |
| `OMNARA_AUTH_PASSWORD_RESET_ENABLED` | Password reset flow | `1` | No |
| `OMNARA_AUTH_CONNECTORS_JSON` | JSON array of social/SSO login connectors | — | No |

GitHub uses `kind:"github"` (endpoints default to github.com; GitHub-compatible providers must supply `issuer`, `authorization_url`, `token_url`, and `userinfo_url`). Google and generic SSO providers — Keycloak, Okta, Auth0, Azure, ZITADEL — use `kind:"oidc"` with issuer discovery. An example is shown below:

```sh theme={null}
OMNARA_AUTH_CONNECTORS_JSON='[
  {"slug":"github","kind":"github","display_name":"GitHub","client_id":"{id}","client_secret":"{secret}"},
  {"slug":"google","kind":"oidc","display_name":"Google","issuer":"https://accounts.google.com","client_id":"{id}","client_secret":"{secret}","email_trust_policy":"verified_email"}
]'
```

## Secret encryption

Secret values are envelope-encrypted with keys from this ring.

| Variable | Purpose | Default | Required |
| - | - | - | - |
| `OMNARA_SECRET_ENCRYPTION_KEYS` | JSON object of key ID to base64 32-byte key (the key ring) | *dev:* insecure `dev-local` key | Yes |
| `OMNARA_SECRET_ENCRYPTION_ACTIVE_KEY_ID` | Key ID used to wrap new writes; must exist in the ring | *dev:* `dev-local` | Yes |

Generate each key with `openssl rand -base64 32`. To rotate: add the new key to the ring, set it active, rewrap stored versions, then remove the old key once nothing references it.

## Skill download capabilities

Skill archive offers use a short-lived capability bound to one machine, skill, and revision.

| Variable | Purpose | Default | Required |
| - | - | - | - |
| `OMNARA_SKILL_DOWNLOAD_SIGNING_KEY` | Base64-encoded 32-byte HMAC key for machine-bound skill download capabilities; generate with `openssl rand -base64 32` and set the same value on API and worker | *dev:* insecure local key | Yes |

## Blob storage

Artifact content lives in S3-compatible object storage; a bucket is required.

Grant the service credentials `s3:GetObject`, `s3:PutObject`, and `s3:DeleteObject`
on the bucket's `artifacts/*` and `skills/*` objects.

| Variable | Purpose | Default | Required |
| - | - | - | - |
| `OMNARA_BLOB_S3_BUCKET` | Bucket for artifact content | *dev:* Compose RustFS bucket `omnara` | Yes |
| `OMNARA_BLOB_S3_REGION` | Bucket region | — | No |
| `OMNARA_BLOB_S3_ENDPOINT` | Custom endpoint for S3-compatible stores; HTTPS outside localhost | — | No |
| `OMNARA_BLOB_S3_ACCESS_KEY_ID` / `OMNARA_BLOB_S3_SECRET_ACCESS_KEY` | Static credentials; set both or neither. Leave empty to use the AWS default credential chain. | — | No |
| `OMNARA_BLOB_S3_USE_PATH_STYLE` | `1` for path-style addressing (RustFS) | `0` | No |

## Memory storage

| Variable | Purpose | Default | Required |
| - | - | - | - |
| `OMNARA_MEMORY_DIR` | Absolute directory on the same persistent filesystem for API, every worker, and maintenance | *dev:* `.local/memory`; Compose: `/var/lib/omnara/memory` | API, worker, and maintenance |

The directory must be writable by the service user and support case-sensitive paths, atomic rename, file/directory sync, and shared advisory locks. File search and scripted edits require a Linux worker with Landlock ABI 2 or newer and seccomp; on macOS, use Compose with its named volume. See [deployment](/self-hosting/deployment#memory-storage) for shared mounts and backups.

Native workers need `rg`, `omnara-sandbox`, and `omnara-chroot-sandbox` on `PATH`. For Debian/Ubuntu, install `sed` and `libcap2-bin`, then run `make install-chroot-sandbox` to install the chroot helper and its fixed filesystem jail. Rerun it after upgrading Omnara or the host's sed, libraries, or locale files. The worker image includes this setup.

Workers also need a writable temporary directory (`TMPDIR`, normally `/tmp`) for memory search. If the container root filesystem is read-only, mount a writable volume there.

Scripted edits require a non-root worker with `SYS_CHROOT` available in its capability bounding set and `no_new_privs` disabled. Only the chroot helper acquires the capability; it drops it before running sed.

## Machine daemon and pools

### API daemon configuration

| Variable | Purpose | Default | Required |
| - | - | - | - |
| `OMNARA_DAEMON_SOCKET_FALLBACK_DRAIN_INTERVAL` | API-side fallback drain floor for connected daemon sockets when Redis wakeups are missed | `30s` | No |
| `OMNARA_DAEMON_SOCKET_FALLBACK_DRAIN_JITTER` | Jitter added to the fallback drain; must not exceed the interval | `10s` | No |
| `OMNARA_DAEMON_RELEASE_URL` | Platform-manifest directory embedded in the hosted `omnarad.sh` installer | `https://releases.omnara.com/omnarad/latest` | No |
| `OMNARA_DEFAULT_MACHINE_POOL_TEMPLATES` | Path to a YAML file of default pool templates; when set, new orgs get cluster-managed machine pools | — | No |
| `OMNARA_UNIKRAFT_DEFAULT_POOL_TOKEN` | Provider token read by the example default-pool template; default-pool provider tokens stay in env | — | No |

Machine pool templates automatically create a base machine pool for any new organization. Templates use the same provider settings as custom pools; see [Sandbox providers](/machines/providers/overview) for each provider.

### Operator-provisioned default model provider

An installation can give each new organization a model provider without storing
the upstream credential in configuration. Leave these variables unset to let
organization owners configure providers themselves.

| Variable | Purpose | Default | Required |
| - | - | - | - |
| `OMNARA_DEFAULT_MODEL_PROVIDER_TEMPLATE` | Path to the provider and model template on the API and maintenance. | — | No |
| `OMNARA_HOSTED_API_URL` | Base URL of the operator credential service. Used by the API and maintenance. | — | With a default provider template |
| `OMNARA_HOSTED_API_TOKEN` | Bearer token shared with the operator credential service; at least 32 bytes. Used by the API and maintenance. | — | With a default provider template |

A template names the provider and lists the models each organization receives:

```yaml theme={null}
provisioner: openrouter
name: omnara-openrouter
credential_secret_name: omnara-openrouter-key
api_format: openai-chat-completions
api_variant: openrouter
base_url: https://openrouter.ai/api/v1
endpoint_path: /chat/completions
auth_kind: bearer_token
models:
  - name: openai/gpt-6.1-sol
    provider_model_slug: openai/gpt-6.1-sol
    context_window_tokens: 1050000
    max_output_tokens: 128000
    supports_tools: true
    supports_reasoning: true
    default_reasoning_effort: medium
    supported_reasoning_efforts: [max, xhigh, high, medium, low]
    input_modalities: [file, image, text]
    output_modalities: [text]
```

Templates support the [token settings and timeouts](/organization/model-providers)
described for model providers.

To reconcile configured defaults into existing organizations, run these commands
with the same database and template environment as the API:

```sh theme={null}
omnara-api reconcile-defaults plan
omnara-api reconcile-defaults apply
```

Review the plan before applying. Reconciliation updates installation-managed
defaults while preserving organization-owned providers and models, along with
explicit project grant overrides.

The credential service contract has these invariants:

* `POST /model-provider-credentials` receives the organization ID, creator user
  ID, and provider template. A successful response is `201` with
  `{"credential_value":"..."}`; `202` and `5xx` are retryable, while `409`
  represents a durable identity or provisioner conflict.
* The organization ID is the stable issuance identity. The same request may be
  repeated until Omnara commits the credential, provider, models, grants, and job
  completion together. The credential service must recover ambiguous issuance
  without returning an unusable credential.
* Organization creation never depends on credential issuance. The API attempts
  provisioning after commit, and maintenance retries failures.
* Apply migrations before deploying both binaries. The API and maintenance must
  use the same template, credential-service configuration, and secret-encryption
  key ring.

### Host daemon configuration

| Variable | Purpose | Default |
| - | - | - |
| `OMNARA_HOME` | Binary, configuration, logs, locks, and runtime state | `$HOME/.omnarad` |
| `OMNARA_API_URL` | API root URL, including the `/api/v1` mount for self-hosted deployments; HTTP is accepted only for loopback or `host.docker.internal` during local development | Existing `daemon.json` value, otherwise `https://api.omnara.com/v1` |
| `OMNARA_MACHINE_TOKEN` | Machine token; prompted for by the installer when omitted | Existing `daemon.json` value |
| `OMNARA_NO_UPDATE` | Set to `1` or `0` to temporarily disable or enable managed updates | Existing `daemon.json` value, otherwise `0` |
| `OMNARA_GIT_CREDENTIALS` | Set to `0` to refuse agent commands that request Git credentials, or `1` to enable the helper; persist with `omnarad install` | Existing `daemon.json` value, otherwise `1` |
| `OMNARA_RUNNER_PATH` | Temporary `PATH` override for agent commands | Existing `daemon.json` value, otherwise the installation `PATH` |
| `OMNARA_DAEMON_SEED_PATH` | Existing `omnarad` binary used only to seed a fresh installer-owned home | — |

## Web tools

Web tools work out of the box: `web_fetch` has no vendor dependency, and `web_search` defaults to Exa's keyless endpoint.

| Variable | Purpose | Default | Required |
| - | - | - | - |
| `EXA_API_KEY` | Upgrades `web_search` to the direct Exa API; recommended for hosted or multi-tenant deployments | — | No |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.