Skip to content

Configuration

StoryOS is configured with environment variables in a .env file next to docker-compose.yml (Compose reads it automatically). Only BETTER_AUTH_SECRET is required.

Variable Default Notes
BETTER_AUTH_SECRET — (required) Generate with openssl rand -hex 32.
POSTGRES_PASSWORD storyos Change for anything internet-facing.
API_URL / WEB_URL http://localhost:3001 / :3000 Set both to your public URL (same origin), e.g. https://os.example.com. WEB_URL is also passed to the mcp service (the origin used to build the url field on MCP record responses) and to web itself (metadataBase, so shared-link previews resolve images against your real domain instead of localhost).
NEXT_PUBLIC_API_URL empty (same-origin) Only for split-origin setups (API on a different host). Set at build time.
API_INTERNAL_URL http://api:3001 The web container’s own server-side route to api over the compose network, used when a page resolves data before rendering (e.g. a shared view’s link-preview metadata). Change only if you’ve renamed the api service.
HTTP_PORT / HTTPS_PORT 80 / 443 Host ports of the Caddy proxy.
SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASS, MAIL_FROM unset Invites, mentions, verification, and resets. Without SMTP, invite links are copyable in the UI and other emails are logged. Any SMTP provider works.
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET unset Enables “Continue with Google”. Redirect URI: {API_URL}/api/v1/auth/callback/google.
STORAGE_DRIVER local s3 for MinIO/S3/R2 — see attachments.
S3_ENDPOINT / S3_BUCKET / S3_ACCESS_KEY / S3_SECRET_KEY MinIO defaults Used when STORAGE_DRIVER=s3.
ATTACHMENT_MAX_BYTES 20971520 (20 MB) Per-file upload cap.
RATE_LIMIT_PER_MINUTE 300 Per token/session.

Integrations: what’s available self-hosted

Section titled “Integrations: what’s available self-hosted”

One codebase, no crippled edition — but a handful of integrations genuinely can’t work the same way on your own instance as on StoryOS Cloud, and the Connections/Integrations gallery says so plainly rather than showing a Connect button that fails. Every provider falls into one of three tiers:

Tier Examples Self-hosted
API key / token Apify, Resend, SMTP, a generic HTTP connection, bring-your-own-AI over MCP Works identically everywhere — nothing StoryOS-specific to configure.
OAuth, needs a verified app Google (YouTube, Calendar) Cloud uses StoryOS’s own verified OAuth app. Self-hosted: bring your own OAuth app via GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET (or the matching env pair for other providers) — an operator, one-time setup, never a per-user prompt. Unconfigured, the gallery shows “you can turn this on”, not a broken button.
Cloud-hosted only Managed StoryOS AI, the hosted MCP OAuth connector Genuinely cloud-only — nothing to configure, because the thing being offered (StoryOS’s own infrastructure) doesn’t exist on your instance by definition.

The reason for the middle tier: an OAuth app’s redirect URI and platform verification are tied to one domain. StoryOS’s own verified Google app is scoped to app.storyos.dev — it can’t also work for your instance, so a self-hosted deployment needing that provider registers its own app instead of asking every end user to.

StoryOS uses better-auth mounted inside the API for email/password (with verification and reset) plus env-gated Google OAuth — instances without Google credentials simply hide the button. Sessions are database-backed for instant revocation. Personal access tokens (mn_pat_…) are a separate, workspace-scoped credential; see authentication.

Email is optional. When SMTP_HOST is unset, invite links are copyable in the UI and other emails are logged rather than sent. Any SMTP provider works — for example Resend:

Terminal window
SMTP_HOST=smtp.resend.com
SMTP_PORT=465
SMTP_USER=resend
SMTP_PASS=re_your_resend_api_key_here
MAIL_FROM=StoryOS <noreply@your-domain.com>

Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET, and register the redirect URI {API_URL}/api/v1/auth/callback/google in the Google console. Accounts link by verified email.

To let cloud clients connect the hosted MCP endpoint with a one-click OAuth connector instead of pasting a token, enable the OAuth authorization server:

Variable Where Notes
MCP_OAUTH api + mcp Set true to turn on OAuth discovery (needs the OIDC tables migrated). Off by default.
MCP_PUBLIC_URL mcp Public URL of the MCP endpoint, e.g. https://mcp.your-domain.com.
MCP_AUTH_SERVER mcp Better Auth’s mounted base URL, e.g. https://app.your-domain.com/api/v1/auth.

Your instance serves its own interactive docs at {API_URL}/api/docs and the raw spec at {API_URL}/api/v1/openapi.json. Create personal access tokens in the app under API tokens — see authentication.