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

# Configuration reference

> How OxiMail configuration works: a TOML file as the boot baseline, with optional database overrides for runtime tuning, and where each setting must live.

This page explains the OxiMail **configuration model**: where settings come
from, what must live in the file versus what can change at runtime, and how a
runtime change takes effect. It is the hub that the per-feature configuration
sections link into. For the exhaustive list of keys in any one area, see the
[example config](#the-config-file-and-its-shape) and the matching operator
page.

## The model: TOML baseline, DB overrides

OxiMail does not store its whole configuration in the database. It reads a
**TOML file at boot** as the baseline, and layers **optional per-organization
database overrides** on top for runtime tuning without a restart. This is
[ADR-007](/reference/adr-index).

The lookup order for a setting is:

1. **Database override** (if one exists for this key) — wins.
2. **TOML file** — the fallback, and the source of truth for everything else.

The reasoning is operability. A TOML file can be read with `cat`, versioned in
git, copied with `scp`, and edited over SSH. A full-database config cannot, and
it creates a chicken-and-egg problem: the database has to be configured before
you can read the configuration that configures the database. OxiMail keeps the
file as the authoritative baseline and uses the database only for runtime
overrides.

<Note>
  The TOML file always remains the source of truth for bootstrap and disaster
  recovery. If you lose the database overrides, the server still boots from the
  file. The reverse is not true.
</Note>

## Bootstrap vs runtime settings

Not every setting can be a database override. Anything OxiMail needs **before
the database is open** has to come from the file. That is the chicken-and-egg
rule:

> Never put bootstrap configuration in the database only.

**Bootstrap settings (TOML only)** — needed before or in order to open storage
and start listening:

* `[storage]` `sqlite_path`, `blob_path`, `search_path`, `encrypted` — where and
  how the database itself lives.
* `[server]` `bind`, `http_bind`, `admin_bind`, `hostname`, `base_url` — the
  listening sockets and identity.
* `[smtp]` `bind`, `submission_bind`, `smtps_bind`, `hostname` — the SMTP
  listening sockets.
* `[auth]` `default_tenant`.

These describe how the process comes up. Putting them in a database override is
meaningless, because the server cannot read the database until they have already
been applied.

**Runtime settings (overridable)** — things you may want to tune on a running
server: rate-limit ceilings, fail2ban thresholds, spam options, and similar
operational knobs. These have a sensible TOML default and can be overridden at
runtime through the admin API (see [Setting a runtime override](#setting-a-runtime-override)).

<Warning>
  A handful of settings are deliberately **restart-only** even though they look
  like runtime knobs. For example, the SRS secret key (`[srs] secret_key`) is read
  once at boot and is not hot-reloadable, so rotating it requires a restart. When
  a section is restart-only, its operator page says so.
</Warning>

## Hot-reload

Runtime overrides are designed to take effect **without restarting** the
process. The mechanism is `ArcSwap`, a lock-free atomic pointer swap
([ADR-016](/reference/adr-index)): the component holds an `Arc` to its current
state, and a reload atomically swaps in a new `Arc`. Readers are never blocked —
in-flight requests finish on the old value, and every subsequent read sees the
new one. This is used for the hot-reloadable components: TLS certificates (so an
ACME renewal applies without a restart), the spam blocklist, runtime config
overrides, and compiled Sieve scripts.

Because reads are never locked, hot-reload has no measurable cost on the request
path: a renewal or an override is a single pointer swap, and reloads are rare.

## Setting a runtime override

Runtime overrides are stored per organization (a *tenant* in the storage model) and set through the admin REST API. See
the [Admin API](/developer/admin-api) for authentication (every admin endpoint
requires the admin bearer token configured under `[admin] token`).

To set one or more overrides, `PUT /admin/v1/config` with a JSON body whose
`overrides` object maps each config key to its new value:

```bash theme={null}
curl -X PUT https://mail.example.com/admin/v1/config \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"overrides": {"limits.max_message_size": "100MB"}}'
```

Each key/value pair is written to the override store for the default tenant.
Values keep their JSON type (a number stays a number, a string stays a string).

<Note>
  Listing the current overrides via `GET /admin/v1/config` is not implemented in
  v0.30.0 — the endpoint returns a usage hint rather than a dump. To inspect what
  is in effect, check the TOML file and the overrides you have set.
</Note>

## The config file and its shape

The server reads `/etc/oximail/oximail.toml` by default. CLI subcommands and the
`serve` command accept `--config <path>` to point elsewhere. The
[setup wizard](/first-boot) writes this file for you on first boot; this page is
for tuning it afterwards.

A representative annotated file looks like this. It is **not** exhaustive — it
shows the shape and the most common sections. See the per-feature operator pages
for the full set of keys in each area.

```toml theme={null}
# /etc/oximail/oximail.toml

[server]
hostname = "mail.example.com"          # server identity
bind = "0.0.0.0:443"                    # HTTPS / JMAP socket
base_url = "https://mail.example.com"  # used in JMAP Session URLs
http_bind = "0.0.0.0:80"               # ACME challenges + HTTPS redirect
# admin_bind = "127.0.0.1:9000"        # optional separate admin port
trusted_proxies = ["127.0.0.1"]        # read X-Forwarded-For from these
allowed_origins = []                   # CORS allowlist (empty = reject all).
                                       # The setup wizard fills this when the
                                       # webmail lives on another origin —
                                       # never solve CORS at the reverse proxy.

[storage]
sqlite_path = "/var/lib/oximail/data.db"
blob_path = "/var/lib/oximail/blobs"
encrypted = true                       # SQLCipher on by default
# pool_size = 32                       # shared SQLite pool; ~8 MiB page cache
                                       # per connection. Raise on read-heavy
                                       # boxes with cores to spare.

[auth]
default_tenant = "default"

[mode]
role = "primary"                       # "primary" or "backup"

[smtp]
bind = "0.0.0.0:25"                    # inbound (receive)
submission_bind = "0.0.0.0:587"        # submission (send, STARTTLS)
smtps_bind = "0.0.0.0:465"             # submission over implicit TLS
hostname = "mail.example.com"
dane_enabled = true                    # outbound DANE (RFC 7672)

# One signing key per sending domain (v0.30 array form).
[[smtp.dkim_keys]]
domain = "example.com"
selector = "default"
key_path = "/etc/oximail/dkim/example.com.default.key"

[tls]
acme_enabled = true                    # Let's Encrypt; off when behind a proxy
acme_email = "admin@example.com"

[security]
fail2ban_max_attempts = 5
fail2ban_ban_minutes = 60
trusted_ips = ["10.0.0.0/8"]           # bypass rate limit AND fail2ban

[rate_limit]
http_per_ip_per_minute = 100
smtp_outbound_per_hour = 50

[spam]
dnsbl_servers = []

[legacy]
enabled = true                         # IMAP / CalDAV / CardDAV bridges
```

### The real top-level sections

The sections that exist in v0.30.0 are: `[server]`, `[storage]`, `[auth]`,
`[mode]`, `[smtp]` (with the `[[smtp.dkim_keys]]` and `[[smtp.transport_maps]]`
arrays), `[tls]`, `[spam]`, `[security]`, `[rate_limit]`
(with the `[rate_limit.destination_domains]` table), `[logging]`, `[admin]`,
`[telemetry]`, `[legacy]`, `[mta_sts]`, `[dns]`, `[sieve]`, `[network]`,
`[distribution_groups]`, `[scheduler]`, `[srs]`, `[metrics]`, `[chat]`, and
`[push]`.

The per-feature pages document the keys inside each:

* [Mail & SMTP](/operator/mail-and-smtp) — `[smtp]`, transport maps, DKIM keys.
* [Email authentication & security](/operator/email-auth-security) — `[mta_sts]`, `[dns]`, `[srs]`.
* [Anti-spam](/operator/anti-spam) — `[spam]`.
* [Mailboxes & storage](/operator/mailboxes-storage) — `[storage]`.
* [Encryption at rest](/operator/encryption-at-rest) — `[storage] encrypted`.
* [TLS & ACME](/operator/tls-acme) — `[tls]`.
* [Legacy protocols](/operator/legacy-protocols) — `[legacy]`.
* [Sieve rules](/operator/sieve-rules) — `[sieve]`.
* [Multi-tenancy](/operator/multi-tenancy) — `[auth]`, `[chat]` capacity caps, and organization overrides.
* [Compliance](/operator/compliance) — journaling rules (stored in the database, managed by CLI/JMAP, not TOML) and the `erasure.reversal_ttl_days` per-organization override.
* [JMAP Core / authentication](/developer/jmap-core) — the `[auth]` DPoP knobs (`dpop_nonce_required`, `dpop_resource = "off" | "monitor" | "require"`).

## Validating a config before you deploy

OxiMail fails loud on a bad config: rather than booting in a half-broken state,
it refuses to start and prints what is wrong. You can run the same validators
ahead of time without starting the server:

```bash theme={null}
oximail check-config --config /etc/oximail/oximail.toml
```

This catches things like a primary server with no DKIM signing key, `dane_enabled`
set with no DNS provider, and deprecated pre-v0.30 DKIM keys still in the file.
See the [CLI reference](/operator/cli) for the full command set and
[Operations](/operator/operations) for the day-to-day workflow.

## Next steps

* [First boot & setup wizard](/first-boot) — what the wizard writes into this file.
* [Admin API](/developer/admin-api) — the endpoint that sets runtime overrides.
* [Operations](/operator/operations) — logs, backups, and upgrades.
* [CLI reference](/operator/cli) — every `oximail` subcommand.
