Skip to content

Environment (.env)

The server reads .env from the repository root with its own small loader (server/config/env.js). A variable that is already set in the process environment (for example by Docker or systemd) wins over the same line in .env. The loader understands plain KEY=VALUE lines and strips one pair of surrounding quotes; it does not expand variables, accept an export prefix or strip inline comments, so CLAUDE_MODEL=claude-sonnet-5 # note makes the comment part of the value.

Boolean flags share one vocabulary: 1, true, yes and on mean on, 0, false, no and off mean off (any case). Anything else, including a typo, keeps the flag’s default. Numeric settings fall back to their default when the value is not a number or out of range. The full annotated list lives in .env.example in the repository.

  • PORT: HTTP port (default 4177)
  • HOST: bind address (default 127.0.0.1; set 0.0.0.0 inside a container)
  • APP_URL: public base URL of the instance (e.g. https://slides.yourcompany.com). Used for absolute links such as share URLs, social cards and emails. Falls back to https://{DOMAIN} if not set; with neither set the server warns at boot and those links end up empty or relative.
  • DOMAIN: public hostname, used as the APP_URL fallback and by the Caddy reverse proxy
  • TRUST_PROXY: true behind a reverse proxy you control, so the client IP is read from X-Forwarded-For / X-Real-IP. The per-IP login throttle keys on that IP. Leave it off on a directly exposed server. TRUSTED_PROXY_COUNT sets how many proxies sit in front (default 1).
  • DATA_DIR, UPLOADS_DIR: data and local-media directories, absolute or relative to the repository root (defaults server/data and server/uploads)

Database settings (DATABASE_URL, DATABASE_*) are on Database.

Auth is on by default. Users are managed in the database; there is no user list in the environment.

  • AUTH_SECRET: required. Session tokens are signed with it, so it must be at least 32 characters (generate one with openssl rand -base64 48). The server refuses to start when it is missing, unless auth is explicitly switched off with AUTH_ENABLED=false or the instance runs in demo or sandbox mode. It also refuses a secret shorter than 32 characters.
  • AUTH_ENABLED: default on. Only an explicit off value (false, 0, no, off) disables auth; a typo leaves it on. With auth off, every visitor is an anonymous admin.
  • AUTH_ADMIN_EMAIL: the user with this email always gets the admin role
  • AUTH_ALLOW_WEAK_SECRET: true lets the server boot with a secret shorter than 32 characters (not recommended; rotate to a strong secret instead)
  • SECURE_COOKIES: true forces the Secure flag on cookies. Requests that arrive over HTTPS (directly or with X-Forwarded-Proto: https) get it regardless.
  • COOKIE_DOMAIN: optional cookie domain, e.g. .yourdomain.com to share the session across subdomains
  • CSRF_ALLOWED_ORIGINS: comma-separated extra origins allowed to send cookie-authenticated state-changing requests. By default only the app’s own origin (Host, APP_URL or DOMAIN) is accepted.
  • AUTH_DEV_BYPASS: true enables a “bypass login” button on /login that signs you in as admin without a password. It only takes effect with NODE_ENV=development; with NODE_ENV=production the server refuses to start while it is set.

Single sign-on with one OIDC provider is configured with SSO_ENABLED, SSO_PROVIDER (oidc), OIDC_ISSUER_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET and OIDC_REDIRECT_URI, plus the optional OIDC_ALLOWED_DOMAINS, OIDC_AUTO_PROVISION, OIDC_DEFAULT_ROLE, OIDC_ADMIN_GROUPS and SSO_ENFORCE (hide password and magic-link login). The server refuses to start when SSO_ENABLED=true but the OIDC settings are incomplete.

See Authentication.

  • DEMO_MODE: true makes a read-mostly showcase install: AI, direct uploads and the Notion compose features are off, and the server boots without AUTH_SECRET (default off)
  • AI_ENABLED: false switches AI features off even when vendor keys are configured (default on). Demo mode and sandbox mode also switch AI off. Not everything checks it: the editor’s AI Analysis (and its endpoint), the public API’s /api/v1/ai/* endpoints and the MCP server’s AI tools still run while a vendor key is configured. Remove the vendor keys to rule out every model call. See Getting started with AI.
  • UPLOADS_ENABLED: false switches direct file uploads off (default on). Demo mode, sandbox mode and IMAGEKIT_ONLY also switch them off.
  • IMAGE_LIBRARY_ENABLED: false switches the local image library off (default on; IMAGEKIT_ONLY also switches it off)
  • MULTI_ORG_ENABLED: true lets one instance hold several workspaces that users can create and switch between (default off)
  • COLLAB_ENABLED: true enables real-time collaborator presence in the editor (default off); COLLAB_LIVE_EDITS adds experimental live document editing on top of it. See Real-time collaboration.
  • LIVE_DATA_ENABLED: true lets slides connect to external data sources (default off)
  • RSS_FEED_ENABLED: false is an instance-wide kill switch for the RSS/Atom/JSON feeds of published presentations (default on; each organization still switches its feeds on itself)

The older disable-form spellings DISABLE_AI, DISABLE_UPLOADS and DISABLE_IMAGE_LIBRARY still work but print a boot warning, and are removed in the first release after 1 November 2026. Replace DISABLE_AI=true with AI_ENABLED=false, and likewise for the other two. When both spellings are set, the *_ENABLED variable wins.

  • SANDBOX_MODE: true enables sandbox behavior (anonymous guest sessions, automatic cleanup after the TTL, watermarked exports; AI, direct uploads and publishing off)
  • SANDBOX_TTL_HOURS: deck TTL in hours (default 24)
  • SANDBOX_DEFAULT_THEME: default theme id (default editorial)
  • SANDBOX_WATERMARK: watermark text on exports (default Sandbox export • Created by an anonymous user)

Guest quotas, cookie lifetime and separate data directories have their own variables; see Sandbox mode.

A daily cleanup trims data that would otherwise grow without bound. It runs once when the server starts and then every 24 hours.

  • TRASH_RETENTION_DAYS: days a trashed presentation stays recoverable before the cleanup deletes it for good, thumbnails included (default 30). The trash page states this number. A whole number of at least 1; 0 or an invalid value falls back to 30, so there is no way to switch the cleanup off. See Trash & Recovery.
  • ACTIVITY_RETENTION_DAYS: days an activity event is kept in the organization feed (default 180). Events carry the actor’s email address.

Upgrading from a version before 1.36.0: the first start deletes every presentation that has already been in the trash longer than TRASH_RETENTION_DAYS. Raise the number before you deploy if you want to keep that backlog. See the 1.36.0 release notes.

A vendor counts as configured when its key is set; openai-compat needs an endpoint and a model instead.

  • LLM_VENDOR: default vendor when several are configured: openai, claude, mistral, deepseek or openai-compat. Unset (or an unknown value): the first configured vendor in that order wins.

OpenAI:

  • OPENAI_API
  • OPENAI_MODEL (default gpt-5.2)

Claude:

  • CLAUDE_API
  • CLAUDE_MODEL (default claude-sonnet-5)
  • CLAUDE_MODEL_PLAN: model for the deck outline step (default claude-opus-4-8, or CLAUDE_MODEL when that is set)

Mistral:

  • MISTRAL_API
  • MISTRAL_MODEL (default mistral-large-latest)

DeepSeek:

  • DEEPSEEK_API
  • DEEPSEEK_MODEL (default deepseek-chat)

OpenAI-compatible endpoint (Ollama, vLLM, Together AI, Fireworks and similar):

  • OPENAI_COMPAT_ENDPOINT: full chat-completions URL, e.g. http://localhost:11434/v1/chat/completions
  • OPENAI_COMPAT_MODEL: model name (required)
  • OPENAI_COMPAT_API: API key (optional; not needed for Ollama)
  • OPENAI_COMPAT_LABEL: display name in the UI (optional)

Other:

  • AI_VALIDATION_LOGGING: false stops writing AI validation events (unknown fields, schema issues) to server/logs/ai-validation (default on)
  • NOTION_SECRET: Notion integration secret; enables Notion fetch, import and publish
  • NOTION_FEATURE: true additionally enables the Notion subjects, compose and suggestion endpoints (default off; ignored in demo mode)

See Notion.

The server can inject third-party analytics tags into the app’s <head>. Only the app itself gets them: published decks and embeds never load third-party analytics. Matomo, Plausible, Umami and GA4 can also be configured in Settings > Analytics; a provider enabled there wins, and the variables below are the fallback. See Analytics for setup.

Common toggles:

  • DISABLE_ANALYTICS: true removes every provider tag
  • ANALYTICS_ALLOW_IN_SANDBOX: true allows provider tags even when sandbox mode is enabled

Providers:

  • GTM_CONTAINER_ID (environment only)
  • MATOMO_URL, MATOMO_SITE_ID (both required), optional MATOMO_DISABLE_COOKIES (default on), MATOMO_REQUIRE_CONSENT (default off), MATOMO_TRACK_LINKS (default on)
  • PLAUSIBLE_DOMAIN, optional PLAUSIBLE_URL (when self-hosting Plausible)
  • UMAMI_WEBSITE_ID, optional UMAMI_URL (when self-hosting Umami)
  • GA4_MEASUREMENT_ID

Escape hatch (environment only):

  • ANALYTICS_HEAD_HTML: raw HTML injected into <head>
  • ANALYTICS_HEAD_HTML_B64: base64 version (recommended to avoid quoting issues; wins when both are set)