Self-hosting

Configuration reference

Every environment variable a self-hosted Desyn installation reads, what it does, whether it is required and where it is set.

On this page

Desyn is configured entirely through environment variables. They are read in three places, shown as Read by under each variable:

  • Convex — the backend functions. They live in the deployment's environment, not in a file: the quickstart keeps them in deploy/compose/convex.env and pushes them with convex env set (see Convex); the Convex dashboard can edit them too.
  • Web app — the Next.js container. Variables starting with NEXT_PUBLIC_ are inlined into the browser bundle at build time, so changing them means rebuilding the image. The quickstart stack derives them from the DESYN_*_URL values in deploy/compose/.env.
  • Worker — the render worker container.

Variables marked secret are credentials. Keep them in your server's environment or a secrets manager, never in git, never in a NEXT_PUBLIC_ variable, and never in logs or screenshots.

Core#

Identity of the installation, its public URLs and the secrets every deployment needs.

DESYN_EDITIONOptional

hosted for desyn.app; any other value (or none) means the self-hosted edition, which unlocks every feature for every account, disables billing and keeps telemetry off by default. Set it explicitly so the web app build and the backend agree. Example: selfhost

Default
selfhost
Read by
Convex, Web app
SITE_URLRequired

Public origin of the Desyn site, used for absolute links. On a self-hosted installation this is usually the same origin as APP_URL; when unset it falls back to it. Example: https://design.example.org

Default
APP_URL
Read by
Convex, Web app
APP_URLRequired

Public origin of the web app. Used for sign-in redirects, email links (verification, magic link, invites) and share links. When unset it falls back to SITE_URL. Example: https://design.example.org

Default
SITE_URL
Read by
Convex, Web app
BETTER_AUTH_SECRETRequiredSecret

Signs sessions and auth tokens. Use at least 32 random bytes (openssl rand -base64 32). Rotating it signs everyone out.

Default
none
Read by
Convex
ADMIN_EMAILSOptional

Hosted edition only: comma-separated email addresses with access to the admin console. On a self-hosted installation the first account becomes an administrator and further admins are promoted in the console. Example: ops@example.org,owner@example.org

Default
none
Read by
Convex
AUTH_TRUSTED_ORIGINSOptional

Additional origins, comma-separated, that may call the authentication routes. APP_URL and SITE_URL are always trusted; add others only if you serve the app from more than one origin. Example: https://design.example.com

Default
none
Read by
Convex
AUTH_IP_HEADERSOptional

Request headers, comma-separated and most trusted first, that carry the client IP for per-IP rate limits and session records. The first value found wins (for X-Forwarded-For, its first entry). List only headers your own proxy overwrites: a header clients can set freely lets them bypass the limits. Behind Cloudflare use cf-connecting-ip,x-real-ip. Example: cf-connecting-ip,x-real-ip

Default
x-real-ip,x-forwarded-for
Read by
Convex
AI_KEY_ENCRYPTION_SECRETRequiredSecret

Encrypts members' own provider keys (BYOK) at rest (AES-GCM). At least 16 characters; use 32 random bytes. If you lose or change it, stored keys can no longer be decrypted and members must enter them again.

Default
none
Read by
Convex
TEMPLATES_PIPELINE_SECRETOptionalSecret

Token that lets the template pipeline publish template bundles (templates/admin:publish) without a signed-in administrator. Leave it unset to allow publishing only from an admin session. If you set it, use 32 random bytes and give it only to the machine that runs the pipeline.

Default
none
Read by
Convex
MCP_ALLOWED_ORIGINSOptional

Extra browser origins allowed to call the MCP endpoint (/mcp), comma-separated (for example https://claude.ai,https://chat.example.com). The app and site URLs are always allowed; requests without an Origin header (desktop and CLI MCP clients) are unaffected.

Default
none
Read by
Convex

Convex#

Where the backend lives. The CLI variables are only needed on the machine that deploys functions.

CONVEX_URLRequired

URL of the Convex deployment's client API (port 3210 on a default self-hosted backend). Server-side code uses this name. Convex provides it to its own functions automatically (from the backend's CONVEX_CLOUD_ORIGIN), so set it only for the web app and the worker. Example: https://convex.example.org

Default
none
Read by
Convex, Worker
NEXT_PUBLIC_CONVEX_URLRequired

The same URL as CONVEX_URL, exposed to the browser. It is inlined when the web app is built, so rebuild after changing it. Example: https://convex.example.org

Default
none
Read by
Web app
CONVEX_SITE_URLRequired

Origin that serves Convex HTTP actions (port 3211 on a default self-hosted backend): authentication routes, /api/v1/*, /mcp, /healthz, /og/:id and the Stripe webhook. Convex provides it to its own functions automatically (from the backend's CONVEX_SITE_ORIGIN); set it yourself only for the web app. Example: https://convex-site.example.org

Default
none
Read by
Convex, Web app
CONVEX_SELF_HOSTED_URLRequired if deploying to a self-hosted backend

Read by the Convex CLI (npx convex deploy) to find your self-hosted backend. Only needed where you run the CLI. Example: https://convex.example.org

Default
none
Read by
CLI only
CONVEX_SELF_HOSTED_ADMIN_KEYRequired if deploying to a self-hosted backendSecret

Admin key of the self-hosted backend, printed by its generate_admin_key.sh script. Grants full control of the deployment; keep it out of the web app's environment.

Default
none
Read by
CLI only

Object storage#

File bytes (uploads, AI results, exports, snapshots) live in S3 or any S3-compatible store.

S3_BUCKETRequired

Bucket that stores uploads, generated assets, exports, thumbnails and design snapshots. Example: desyn

Default
none
Read by
Convex, Worker
AWS_REGIONRequired

Region of the bucket (and of SES when MAIL_TRANSPORT=ses). S3-compatible stores that ignore regions still need a value such as us-east-1; Cloudflare R2 uses auto. Example: eu-north-1

Default
none
Read by
Convex, Worker
AWS_ACCESS_KEY_IDRequiredSecret

Access key for the bucket (and SES). Scope it to the bucket and, if used, ses:SendRawEmail.

Default
none
Read by
Convex, Worker
AWS_SECRET_ACCESS_KEYRequiredSecret

Secret for AWS_ACCESS_KEY_ID.

Default
none
Read by
Convex, Worker
AWS_SESSION_TOKENOptionalSecret

Session token for temporary AWS credentials (STS). Leave unset with long-lived access keys, which is the usual setup.

Default
none
Read by
Convex
S3_ENDPOINTOptional

Custom endpoint for S3-compatible storage such as MinIO or Cloudflare R2. Leave unset for AWS S3. Browsers upload directly to presigned URLs on this endpoint, so it must be reachable from members' browsers. Example: https://s3.example.org

Default
AWS S3
Read by
Convex, Worker
S3_FORCE_PATH_STYLEOptional

Set to true for stores that need path-style URLs (endpoint/bucket/key), which includes MinIO. Example: true

Default
false
Read by
Convex, Worker
S3_PUBLIC_BASE_URLOptional

Public base URL (for example a CDN) for objects that are safe to serve publicly, such as template thumbnails. When unset, every read uses a short-lived signed URL. Example: https://cdn.example.org

Default
none
Read by
Convex

Email#

Verification, magic links, password resets, invites and receipts.

MAIL_TRANSPORTRequired

ses (Amazon SES through the AWS credentials above), smtp (any SMTP server via SMTP_URL) or log. With log — also used when the value is unset or unknown — messages are written to the function logs instead of being sent, so nobody can confirm their email. Use it only for local development. Example: smtp

Default
log
Read by
Convex
SES_FROMRequired

Sender of every outgoing message, for both transports, as Name <address>. The address must be verified in SES, or accepted by your SMTP server. MAIL_FROM is accepted as an alias and wins when both are set. Example: Desyn <design@example.org>

Default
Desyn <hello@desyn.app>
Read by
Convex
SMTP_URLRequired if MAIL_TRANSPORT=smtpSecret

Connection URL for the SMTP transport. Use smtps:// for implicit TLS (port 465) or smtp:// for STARTTLS (port 587). Percent-encode special characters in the password. Example: smtps://user:password@smtp.example.org:465

Default
none
Read by
Convex
SES_CONFIGURATION_SETOptional

Name of an SES configuration set to send with, for delivery, bounce and complaint tracking in AWS.

Default
none
Read by
Convex

AI providers#

Server-side provider keys. All are optional: without them, members can still use their own keys (BYOK) or local models.

OPENROUTER_API_KEYOptionalSecret

Server key for text and vision models through OpenRouter. Claude tasks fall back to it when ANTHROPIC_API_KEY is not set or the Anthropic API is unavailable; members can also pick any OpenRouter model per task.

Default
none
Read by
Convex
ANTHROPIC_API_KEYOptionalSecret

Server key for Claude on the Anthropic API: the default route for the agent, planning, critique, vision, SVG, copy and translation (Claude Sonnet, Opus and Haiku 5.5). Without it those tasks use OpenRouter.

Default
none
Read by
Convex
OPENAI_API_KEYOptionalSecret

Optional server key for OpenAI models on the OpenAI API. When set, members can pick OpenAI models per task in Settings → AI, billed to platform credits.

Default
none
Read by
Convex
GOOGLE_GENERATIVE_AI_API_KEYOptionalSecret

Optional server key for Gemini models on the Google Generative AI API. When set, members can pick Gemini models per task in Settings → AI, billed to platform credits.

Default
none
Read by
Convex
FAL_AI_API_KEYOptionalSecret

Server key for media models on Fal: image generation, background removal, inpaint, outpaint, upscale and video. FAL_KEY is accepted as an alias.

Default
none
Read by
Convex
OLLAMA_BASE_URLOptional

Default base URL offered for the Ollama provider. Members can override it in Settings → AI providers. Example: http://ollama:11434

Default
none
Read by
Convex, Web app

Render worker#

The Rust worker renders exports, thumbnails and previews for the AI.

RENDER_WORKER_URLRequired

Internal URL of the render worker. Convex actions call it for exports, thumbnails, layout and the previews the agent looks at. Do not expose the worker publicly. Example: http://worker:8080

Default
none
Read by
Convex
RENDER_WORKER_SECRETRequiredSecret

Shared secret sent in the x-desyn-secret header. Set the same value on Convex and on the worker.

Default
none
Read by
Convex, Worker
FONTS_DIROptional

Directory of font files the worker loads at start. Design-specific and custom fonts are fetched per request from asset URLs. Example: /usr/share/desyn/fonts

Default
bundled OFL font set
Read by
Worker

Sign-in providers#

Optional OAuth providers. A provider's button appears only when its credentials are set.

GOOGLE_CLIENT_IDOptional

OAuth client id for Sign in with Google. Register APP_URL/api/auth/callback/google as the authorized redirect URI.

Default
none
Read by
Convex
GOOGLE_CLIENT_SECRETRequired if GOOGLE_CLIENT_ID is setSecret

OAuth client secret for Sign in with Google.

Default
none
Read by
Convex
GITHUB_CLIENT_IDOptional

OAuth app client id for Sign in with GitHub. Set the authorization callback URL to APP_URL/api/auth/callback/github.

Default
none
Read by
Convex
GITHUB_CLIENT_SECRETRequired if GITHUB_CLIENT_ID is setSecret

OAuth app client secret for Sign in with GitHub.

Default
none
Read by
Convex
APPLE_*Optional

Reserved for Sign in with Apple. Apple sign-in is not available yet, so leave these unset; the desktop and iOS apps sign in through the browser with email, magic link, Google or GitHub.

Default
none
Read by
Convex
NATIVE_REDIRECT_URISOptional

Extra redirect URIs, comma-separated, allowed for the desktop, iOS and CLI sign-in flow. desyn://auth/callback and loopback addresses are always allowed.

Default
none
Read by
Convex

Billing (hosted edition)#

Stripe is used only by the hosted edition. Self-hosted installations leave these unset.

STRIPE_SECRET_KEYRequired if DESYN_EDITION=hostedSecret

Stripe API key for Checkout, the Customer Portal and subscription status.

Default
none
Read by
Convex
STRIPE_WEBHOOK_SECRETRequired if DESYN_EDITION=hostedSecret

Signing secret of the webhook endpoint at CONVEX_SITE_URL/stripe/webhook.

Default
none
Read by
Convex
STRIPE_PRICE_PRO_MONTHLYRequired if DESYN_EDITION=hosted

Stripe price id of the monthly Pro plan.

Default
none
Read by
Convex
STRIPE_PRICE_PRO_YEARLYRequired if DESYN_EDITION=hosted

Stripe price id of the yearly Pro plan.

Default
none
Read by
Convex

Stock media#

Optional stock photo sources for the Photos panel and the agent's search_stock tool.

UNSPLASH_ACCESS_KEYOptionalSecret

Enables Unsplash search. Attribution is stored with every photo you place.

Default
none
Read by
Convex
PEXELS_API_KEYOptionalSecret

Enables Pexels search. Attribution is stored with every photo you place.

Default
none
Read by
Convex

Telemetry#

All telemetry is optional and off when unset. With DESYN_EDITION=selfhost it stays off unless you configure it.

NEXT_PUBLIC_POSTHOG_KEYOptional

PostHog project key for product analytics in the browser. Analytics stay off when unset.

Default
none
Read by
Web app
NEXT_PUBLIC_POSTHOG_HOSTOptional

PostHog ingestion host. Defaults to the EU region.

Default
https://eu.i.posthog.com
Read by
Web app
POSTHOG_PROJECT_API_KEYOptionalSecret

PostHog key for server-side events (for example completed exports and AI runs).

Default
none
Read by
Convex
POSTHOG_HOSTOptional

PostHog host for server-side events. Falls back to NEXT_PUBLIC_POSTHOG_HOST.

Default
https://eu.i.posthog.com
Read by
Convex
NEXT_PUBLIC_DATAFAST_WEBSITE_IDOptional

DataFast website id for privacy-friendly traffic analytics.

Default
none
Read by
Web app
NEXT_PUBLIC_DATAFAST_DOMAINOptional

Domain registered with DataFast for this installation.

Default
none
Read by
Web app
DATAFAST_API_KEYOptionalSecret

DataFast API key for server-side goal tracking.

Default
none
Read by
Convex
AXIOM_TOKENOptionalSecret

Axiom ingest token. When set together with AXIOM_DATASET, structured logs are shipped to Axiom; otherwise they stay in local logs.

Default
none
Read by
Convex, Web app, Worker
AXIOM_DATASETRequired if AXIOM_TOKEN is set

Axiom dataset that receives logs. Logs are shipped only when both AXIOM_TOKEN and AXIOM_DATASET are set. Example: desyn

Default
none
Read by
Convex, Web app, Worker
AXIOM_ORG_IDOptional

Axiom organization id (needed for personal tokens).

Default
none
Read by
Convex, Web app, Worker
AXIOM_URLOptional

Axiom API endpoint, for example a regional endpoint.

Default
https://api.axiom.co
Read by
Convex
LOG_LEVELOptional

Minimum level of backend logs: debug, info, warn or error.

Default
info
Read by
Convex

Minimal configuration#

A working installation needs only the Core, Convex, Object storage, Email and Render worker groups. Everything else is optional:

  • No AI keys: the editor works fully; members can still add their own provider keys or point Desyn at a local model. See AI providers.
  • No OAuth providers: members sign in with email and password or magic links.
  • No stock keys: the Photos panel shows only uploads and brand assets.
  • No telemetry variables: nothing leaves your server. See telemetry.

Generating secrets#

Use a cryptographically random value for each secret, and a different one for each:

Shell
openssl rand -hex 32

Docs site variables#

If you also host this documentation site (apps/docs), two optional build variables apply:

VariableDefaultDescription
DOCS_BASE_PATH(empty)Serve the docs under a sub-path such as /docs. Set at build time.
DOCS_SITE_URLhttps://docs.desyn.appCanonical origin used for the sitemap and metadata.

Popular pages

QuickstartGet started
Keyboard shortcutsEditor
The agentAI
Docker Compose quickstartSelf-hosting
Configuration referenceSelf-hosting
Agent API (v1)Developers