Setup

Advanced configuration

Every environment variable of the core and client stacks, with defaults and when to override them.

The three required values (TYTO_VERSION, SERVER_DOMAIN, CORS_ALLOW_ORIGIN) are covered in Configuration. This page is the full reference: every variable both stacks read, its default, and when to touch it. The same list ships annotated as .env.prod.example in each repo — that file is authoritative for your pinned version.

Core stack

Required

TYTO_VERSION

Image tag to run — pin a release (never latest in production). One value pins core and, in the bundle shape, the client image with it.

SERVER_DOMAIN

Public domain of the core API. Drives auto-HTTPS certificate issuance and every derived public URL below.

CORS_ALLOW_ORIGIN

Origins of your web clients — space- or comma-separated exact origins, or a ^-anchored regex. Empty allows no cross-origin browser clients; native desktop/mobile clients don’t use CORS and are unaffected.

Reverse proxy & ports

BEHIND_PROXY boolean default false

Set true behind an existing reverse proxy: the stack serves plain HTTP, issues no certificates, and TRUSTED_PROXIES defaults to private ranges. Combine with HTTP_PORT.

HTTP_PORT / HTTPS_PORT default 80 / 443

Host ports the stack binds. Change when 80/443 are taken — e.g. HTTP_PORT=8080 with your proxy pointing there.

TRUSTED_PROXIES

CIDRs of trusted reverse proxies. Empty when FrankenPHP terminates TLS itself; with BEHIND_PROXY=true defaults to private ranges — set your proxy’s CIDR to narrow it.

TRUSTED_HOSTS default empty

Host-header allowlist regex. Empty by default — any Host is accepted. Recommended to set in production; ^${SERVER_DOMAIN}$ is the suggested value.

CADDY_GLOBAL_OPTIONS

Raw Caddy global options — e.g. email you@example.com to attach an ACME contact for certificate-expiry notices. Certificates issue fine without it.

Bundled client

APP_DOMAIN

Bundle shape only (compose.bundle.yaml): public domain of the bundled web client, served by this stack’s Caddy. Leave empty when the client runs as its own stack.

Public URLs & real-time

DEFAULT_URI / SERVER_API_URL derived from SERVER_DOMAIN

Set explicitly only when the public URLs differ from the hostname — typically behind a proxy that serves the stack on a different scheme or port than SERVER_DOMAIN implies.

MERCURE_CORS_ORIGINS default

CORS for the embedded Mercure hub (SSE). Subscriptions are authorized by JWT, not origin, so

is safe — set a space-separated origin list to restrict anyway.

Features

API_DOCS_ENABLED boolean default true

Interactive API reference (Swagger UI) at /api. false disables the docs UI; the API itself is unaffected.

APP_FALLBACK_LOCALE default en

Fallback for server-rendered content (welcome messages, push notifications, emails) when a community or user locale has no translation catalog.

VOICE_ENABLED boolean default true

false runs a text-only server — hides all voice UI and refuses voice operations server-side. Use it on 443-only hosts where LiveKit’s UDP ports can’t be opened.

Mail

MAILER_DSN

Optional env-level fallback transport only — real SMTP lives in the admin panel (stored in the DB, password encrypted). Empty means mail is discarded until SMTP is configured in the panel.

APP_SMTP_KEY derived from APP_SECRET

Encryption key for the DB-stored SMTP password. Set a stable 32-byte hex value (openssl rand -hex 32) so the stored secret survives an APP_SECRET rotation.

Data services

DATABASE_URL derived

Full DSN override for an external/managed database. Empty derives mysql://tyto:<generated>@database:3306/tyto.

MARIADB_PASSWORD / MARIADB_ROOT_PASSWORD / MEILI_MASTER_KEY auto-generated

Generated into the shared secrets volume on first boot. A non-empty value here always overrides the generated one — the path for managed services.

CACHE_DSN default redis://redis:6379

Redis cache pool (redis:// scheme). Defaults to the bundled sidecar.

Voice (LiveKit)

LIVEKIT_PUBLIC_URL derived from SERVER_DOMAIN

The signal WebSocket is proxied by the app on /rtc, so this derives automatically. Set it only for an external LiveKit with its own TLS. Voice media needs 7881/tcp + 50000–50100/udp open — see Voice setup.

LIVEKIT_INTERNAL_URL default http://livekit:7880

Where the app reaches LiveKit’s API inside the compose network.

LIVEKIT_API_KEY / LIVEKIT_API_SECRET auto-generated

Override only for an external LiveKit deployment.

Web push

VAPID_SUBJECT

Contact URI stamped into Web Push authentication, e.g. mailto:admin@example.com. The VAPID key pair itself is auto-generated.

Auto-generated app secrets

APP_SECRET, JWT_PASSPHRASE, MERCURE_JWT_SECRET, MERCURE_SUBSCRIBER_JWT_KEY, APP_MEDIA_SIGNING_KEY, VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY — all generated into the secrets volume on first boot by the secrets-init service. Override in .env only if you must pin them across hosts.

Danger

The secrets volume is state: losing it invalidates sessions (JWT keys), signed media URLs and push subscriptions, and can orphan the encrypted SMTP password. Include it in backups.

Client stack

Only for the separate-host shape — the bundle serves the client from the core stack and needs none of these beyond APP_DOMAIN above.

TYTO_VERSION

Client image tag — keep it on the same release as the backend.

SERVER_DOMAIN

Public domain serving the SPA. Auto-HTTPS unless BEHIND_PROXY=true.

API_DOMAIN

Domain of your core API (its SERVER_DOMAIN). The SPA discovers everything else from https://<API_DOMAIN>/api/server-info.

SERVER_INFO_URL

Advanced override: full server-info URL for a custom scheme or port. Takes precedence over API_DOMAIN.

BEHIND_PROXY / HTTP_PORT / HTTPS_PORT / CADDY_GLOBAL_OPTIONS

Same semantics as the core stack’s variables above.

Note

Everything not listed here — branding, registration, legal documents, rate limits, retention, upload limits, SMTP, bots — lives in the admin panel’s settings registry, not in env.