Self-hosting

Configuration

Operator settings live in config.toml; secrets stay in env. CORS, TLS, SMTP, and how settings resolve.

2 min read

Non-secret settings (public URL, CORS, ports, TTLs) live in config.toml — the installer puts it in your install directory and compose mounts it. Secrets stay in .env. Settings resolve in this order:

  1. EDDYTOR__SECTION__KEY env override (e.g. EDDYTOR__SERVER__PUBLIC_URL=https://app.example.com)
  2. $EDDYTOR_CONFIG_FILE path
  3. ./eddytor.toml
  4. /etc/eddytor/config.toml
  5. Built-in defaults

The shipped config.toml documents every section and field inline. Restart the containers after editing — config is read at boot.

Required env

Three secrets, all generated into .env by the installer. Sign-in tokens are minted by Eddytor's built-in OAuth 2.1 IdP (ES256, keys stored in the database) — there is no separate JWT signing secret to supply.

VariablePurpose
EDDYTOR_DATABASE_URLPostgres connection string.
EDDYTOR_ENCRYPTION_KEY32-byte base64 AES master key. Generate via openssl rand -base64 32.
EDDYTOR_API_KEY_SECRETHMAC secret for API-key hashing — rotating it invalidates all API keys.

In compose, back up .env — losing EDDYTOR_ENCRYPTION_KEY means losing every stored secret.

Exposing beyond localhost

The compose stack binds the API ports to loopback by default. Set EDDYTOR_BIND_ADDR=0.0.0.0 in .env to reach it from your LAN/VPN — but put TLS in front first (see below). Postgres, Garage, and the engine always stay loopback-only regardless of this setting.

CORS

server.cors.allowed_origins is an allowlist of origin patterns — literal, or glob via * (multi-level, matches across dots):

[server.cors]
allowed_origins = [
  "https://app.example.com",   # exact
  "https://*.example.com",     # any subdomain
  "http://localhost:*",        # any localhost port (dev)
]
allow_credentials = true
Keep globs specific

Every glob must include a literal label after the *https://*.example.com is safe; https://*.com allows every .com domain on the internet. The server validates this at boot.

TLS

Eddytor's binaries speak plaintext everywhere, and TLS is terminated at the edge: a reverse proxy or load balancer (Nginx, Envoy, ALB, Traefik, Caddy) in front of the server's :8080, or a service mesh in Kubernetes. Set server.public_url to the HTTPS hostname your edge serves. There is no internal cert mesh to manage.

Email (SMTP)

Magic-link sign-in sends a link by email. With no SMTP configured (the default), links are written to the server logs instead — fine for a solo operator, but to let teammates log in set EDDYTOR_SMTP_HOST, _PORT, _USER, _PASS, _FROM in .env (SES SMTP, Postfix, Mailgun, …).

Still stuck? We reply fast.

Can't find it? Ask support and a human will answer, usually within a few hours.