---
title: "Configuration"
description: "All configuration is via environment variables."
---

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

# Configuration

Octarq is configured entirely through environment variables — the canonical list
lives in [`.env.example`](https://github.com/octarq-org/octarq/blob/main/.env.example).
The primary configuration options are:

## Core

| Variable | Purpose |
| --- | --- |
| `OCTARQ_SECRET_KEY` | Signs session cookies and encrypts stored credentials. **Required.** Must be at least 16 bytes; once a domain is registered, this length floor is strictly enforced and the instance will refuse to start if shorter. |
| `OCTARQ_ADMIN_USER` | First admin username (default `admin`). |
| `OCTARQ_ADMIN_PASSWORD` | First admin password. **Required.** |
| `OCTARQ_ALLOW_PRIVATE_WEBHOOKS` | Allow webhook & notification delivery to private/loopback IPs. Default `false`. |
| `OCTARQ_ALLOW_PRIVATE_SMTP` | Allow outbound SMTP mail delivery to private/loopback IPs (e.g. local Postfix or Mailhog). Default `false`. |
| `OCTARQ_TRUST_PROXY` | Honour `X-Forwarded-For` / `X-Real-IP` / `X-Forwarded-Proto`. Enable only behind a reverse proxy you control. Default `false`. |
| `OCTARQ_LISTEN` | Address the binary binds (default `:8080`). |
| `OCTARQ_PORT` | **Docker Compose only** — the host port published by `docker-compose.yml` (default `8080`), also the port knob for `make dev`. **Not read by the binary**; to change the binary's own listen port use `OCTARQ_LISTEN`. |
| `OCTARQ_CORS_ORIGINS` | Comma-separated allowlist of exact origins (e.g. `https://app.example.com`) allowed to query public GET endpoints cross-origin. Empty = CORS disabled. Bootstrap fallback for runtime setting. |

## Runtime Instance Settings (Dashboard)

Instance administrators can configure deployment-wide settings live via the UI (**Settings → Instance** / `/instance/settings`), without restarting the server:

| Setting Key | UI Label | Purpose |
| --- | --- | --- |
| `app_name` | Product name | Brand/Product name displayed across the UI (empty = default `octarq`). |
| `base_domain` | Tenant base domain | Automatic tenant subdomain base (`<slug>.<base>`). |
| `shared_hosts` | Shared hosts | Trusted hostnames for public origin derivation. |
| `data_retention_days` | Keep click history for (days) | Auto-cleanup retention period for short link clicks (0 = keep forever). |
| `public_cors_origins` | Public CORS Origins | Comma/newline-separated allowlist of exact origins for public GET endpoints. |
| `mail_system_sender_id` | System SMTP Sender | SMTPSender ID used for system transactional mail (verification, password resets, invites). |
| `reserved_slugs` | Reserved Short Link Slugs | Slugs users are blocked from registering (built-ins like `admin`, `api`, `assets`, `portal` are always reserved). |
| `metrics_token` | Metrics token | Bearer token required for `/metrics` endpoint (empty = loopback only). |
| `ratelimit_auth_rpm` | Auth RPM / IP | Per-IP rate limit for authentication endpoints (default 60). |
| `ratelimit_api_rpm` | API RPM / IP | Per-IP rate limit for general API endpoints (default 600). |
| `ratelimit_redirect_rpm` | Redirect RPM / IP | Per-IP rate limit for short link redirects (default 6000). |
| `allow_registration` | Allow public sign-up | Controls public registration on login page (Settings → Authentication). |
| `require_email_verification` | Require Email Verification | Blocks login until user verifies email (Settings → Authentication). |
| `oauth.google.*` / `oauth.github.*` | Google / GitHub OAuth | Third-party social login client IDs and secrets (Settings → Authentication). |

## Hostnames

There is nothing to configure. Absolute links — password reset, email
verification, workspace invites, OAuth `redirect_uri` — are built from the
hostname the request arrived on, and that hostname is accepted only when it
matches a domain registered under **Domains**. A hostname that matches none
produces relative links instead, so a forged `Host` header can never aim a
password-reset link at somebody else's site.

An instance with no registered domain has nothing to check against and uses the
request host as sent; register the domain you serve on to close that gap.

### Hostname environment variables (optional)

Two optional bootstrap-fallback variables exist for shared / tenant-subdomain
hosts. Both are read at startup only; once saved, the dashboard settings
(Settings → Instance) take over:

| Variable | Purpose |
| --- | --- |
| `OCTARQ_SHARED_HOSTS` | Comma-separated hostnames the dashboard answers on. **Required when tenant subdomains are in use**: once any domain is registered, absolute links (password reset, invites) are only built for registered or declared hosts — without this the shared host builds no absolute URLs. |
| `OCTARQ_BASE_DOMAIN` | Shared tenant-subdomain base (bootstrap fallback): every new org gets `<slug>.<base>` automatically. Needs a wildcard DNS record and a wildcard TLS cert on the base, plus `OCTARQ_SHARED_HOSTS` declaring the base itself. |

Two things follow from the same request rather than from configuration:

- **Where the dashboard is served.** Every hostname serves `/admin`, except one
  registered as a short-link or mail host — those exist for a workspace's public
  traffic and do not show a login form.
- **The session cookie's `Secure` attribute.** Set when the request arrived over
  HTTPS. `X-Forwarded-Proto` counts only with `OCTARQ_TRUST_PROXY` on.

**OAuth operators:** `redirect_uri` now varies by hostname, so every hostname
you offer social login on must be registered with the provider as
`https://<host>/auth/callback/<provider>`.

## Database

octarq defaults to pure-Go SQLite (no cgo). Switch to PostgreSQL or MySQL 8 with two environment variables:

| Variable | Purpose |
| --- | --- |
| `OCTARQ_DB_DRIVER` | `sqlite` (default), `postgres`, or `mysql`. |
| `OCTARQ_DB_DSN` | Connection string when using Postgres (e.g. `postgres://user:pass@localhost:5432/octarq?sslmode=disable`) or MySQL 8 (e.g. `user:pass@tcp(localhost:3306)/octarq?charset=utf8mb4&parseTime=True&loc=Local`). |
| `OCTARQ_REDIS_URL` | Optional Redis connection (e.g. `redis://localhost:6379`) for rate limiting / caching. Empty = those features fall back to DB / in-memory. |

## Email inbound

Inbound mail is delivered by the Cloudflare Email Worker
([`deploy/cloudflare-email-worker.js`](https://github.com/octarq-org/octarq/blob/main/deploy/cloudflare-email-worker.js)).
It needs one variable — set on the **Worker**, not on octarq:

| Variable (Worker) | Purpose |
| --- | --- |
| `OCTARQ_ENDPOINT` | Your **Inbound Webhook URL**, copied from the octarq dashboard (Settings). It already embeds your org's inbound token — no separate token variable. |

## GeoIP (optional)

Geo data for click analytics. Easiest path: set a free MaxMind license key and
octarq auto-downloads GeoLite2-City into its data dir, reusing the cached file on
later starts.

| Variable | Purpose |
| --- | --- |
| `OCTARQ_MAXMIND_LICENSE_KEY` | Free MaxMind key — enables auto-download of GeoLite2-City. |
| `OCTARQ_GEOIP_DB` | Explicit path to an `.mmdb` file — takes precedence over the cached/downloaded one. Unset both = geo disabled. |

## LLM (AI features)

The MCP server's own tools need no LLM, but AI features (such as AI email summaries and AI assistance) can be configured via environment variables using your own provider key (BYOK):

| Variable | Purpose |
| --- | --- |
| `OCTARQ_LLM_PROVIDER` | `claude`, `openai`, `gemini`, `mistral`, `cohere`, or `ollama`. |
| `OCTARQ_LLM_API_KEY` | Your own provider key — octarq never marks up tokens (BYOK). |

:::tip
Defaults are `claude-opus-4-8` for reasoning and `claude-haiku-4-5` for cheap
classification. Switch vendor by name — no per-vendor code.
:::

Source: https://docs.octarq.org/configuration/index.mdx
