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. The shipped .env.example keeps every comment on its own line, so its lines can be uncommented as they are.

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.

After editing .env, and after every upgrade, run npm run doctor (in a container: docker compose exec app node scripts/doctor.js). It checks the configuration without starting the server and prints one line per check, with the fix for anything red. See Docker deployment.

  • 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).
  • APP_LOGO_URL: your logo, shown above every sign-in card and in place of the Deckyard logo in the overview topbar. An absolute http(s) URL or a root-relative path, such as a file under custom/assets/. Unset, the sign-in card has no logo and the topbar shows Deckyard’s.
  • DATA_DIR, UPLOADS_DIR: data and local-media directories, absolute or relative to the repository root (defaults server/data and server/uploads)

For uploaded media, see storage mode (MEDIA_STORAGE_MODE) and S3 configuration (S3_*). To connect an ImageKit photo archive, see ImageKit configuration.

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. On a fresh install (empty user table, auth on) the server creates this address as the first admin at startup; see Your first admin
  • 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, OIDC_GROUPS_CLAIM (which claims hold the groups), OIDC_ORG_CLAIM (which claim names the organization), OIDC_EXTRA_SCOPES (scopes on top of openid email profile), SSO_ENFORCE (refuse password and magic-link login, and password resets) and SSO_BUTTON_LABEL (the words on the SSO button). 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 organizations that people switch between (default off); MULTI_ORG_USER_CREATE_ENABLED false keeps creating them to instance admins. See Multiple Organizations.
  • 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.

Server-rendered PDF and PNG exports run in headless Chrome. The settings below apply to both formats.

  • EXPORT_RENDER_TIMEOUT_MS: timeout in milliseconds for each export render, page load and capture alike (default 120000; 0 disables the cap, not recommended). Raise it when large decks with many images fail with “Timed out after waiting 30000ms”.
  • EXPORT_IMAGE_COMPRESSION: off stops downsampling and recompressing images before they are embedded (default on). Compressed exports are typically an order of magnitude smaller.
  • EXPORT_IMAGE_MAX_PX: longest edge of an embedded image in pixels (default 2600; 0 disables the cap)
  • EXPORT_IMAGE_QUALITY: JPEG quality for opaque images, 1 to 100 (default 80)
  • EXPORT_IMAGE_RETINA_SCALE: an image is embedded at most this many times the size it is drawn on the slide, never above EXPORT_IMAGE_MAX_PX (default 2, range 1 to 4). Raise it if small images look soft when zoomed into the PDF.

Before 1.50.0 these were called PDF_EXPORT_TIMEOUT_MS, PDF_EXPORT_IMAGE_COMPRESSION, PDF_EXPORT_IMAGE_MAX_PX, PDF_EXPORT_IMAGE_QUALITY and PDF_EXPORT_IMAGE_RETINA_SCALE. The old names are not read any more: an instance that still sets one gets the default. See the 1.50.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)