Vibe Coding
Back to Multica

Multica / Self-deployment and reference

environment variables

List of environment variables that need to be configured for the self-host Multica server.

environment variables key concepts infographic
environment variables key concepts infographic

Overview

Multica's self-deployment reads configuration from environment variables when the server starts - database, login, email, storage, and registration whitelist are all configured here. This page is grouped into complete lists by purpose: each group explains what will happen if it is not installed, and which ones are necessary for production. How do those related to Auth really match the login and registration configuration.

Core server environment variables

These are the core variables you must consider before deploying - some have default values to enable the server to start, but in a production environment you should explicitly configure the required ones.

Environment variables Default value Must be set for production?
DATABASE_URL postgres://multica@localhost:5432/multica?sslmode=disable YES
PORT 8080 No (unless changing port)
JWT_SECRET multica-dev-secret-change-in-production Yes (default is unsafe)
APP_ENV empty yes (must be production)
FRONTEND_ORIGIN Empty Yes (self-host should fill in your own domain name)
MULTICA_DEV_VERIFICATION_CODE Empty No (must remain empty for production)

**Production environments keep MULTICA_DEV_VERIFICATION_CODE empty. ** Fixed local test verification codes being turned off by default; if you set MULTICA_DEV_VERIFICATION_CODE=888888, when APP_ENV is not in production, anyone who can request a verification code can log in with this fixed value. This shortcut code will be ignored when APP_ENV=production.

Database connection pool

Environment variables Default value Description
DATABASE_MAX_CONNS 25 The maximum number of connections for pgxpool. Daemon polling frequently (every 3 seconds) consumes connections; large-scale deployments may need to be increased
DATABASE_MIN_CONNS 5 Minimum resident connection

When not set uses the default value in the above table, not pgx's built-in 4/NumCPU - the latter has caused the connection pool to be exhausted in production.

How to configure email

Multica supports two email sending channels - Resend is suitable for public network deployment, and SMTP relay is suitable for intranet/self-deployment. When set simultaneously, SMTPHOST has higher priority than RESENDAPIKEY.

Resend

Environment variables Default value Description
RESEND_API_KEY Empty Resend API key
RESEND_FROM_EMAIL noreply@multica.ai Sending address (must be a domain name that has been verified by the Resend account; it also serves as the From: header when using SMTP)

SMTP relay

Environment variables Default value Description
SMTP_HOST Null SMTP relay host name. Once set, SMTP mode is enabled and overrides Resend
SMTP_PORT 25 SMTP port. Use 587 for STARTTLS submission port; use 465 for SMTPS (implicit TLS, automatically enabled)
SMTP_USERNAME Empty SMTP username. Leave blank to indicate unauthenticated relay
SMTP_PASSWORD Empty SMTP password
SMTP_TLS starttls TLS mode. implicit (alias smtps, ssl) performs an immediate TLS handshake (SMTPS) on connection; port 465 is automatically enabled. If not set / starttls will be upgraded via STARTTLS after connecting
SMTP_TLS_INSECURE false Set to true to skip TLS certificate verification (private CA/self-signed certificates only)
SMTP_EHLO_NAME Machine hostname EHLO/HELO name advertised to relay. When a strict relay (such as Google Workspace smtp-relay.gmail.com) refuses the default greeting from a public IP, fill in a real FQDN - otherwise the relay will directly disconnect and behave as an incomprehensible EOF on a subsequent command

The server will automatically upgrade when advertising STARTTLS. The dial timeout is 10s, and the entire SMTP session has a 30s deadline to prevent the relay black hole from hanging up the auth handler.

Neither behavior: The server will not report an error, but all emails (verification codes, invitation links) that should be sent only hit the stdout of the server. Local development is convenient (you copy the verification code from the server log); If you forget to set up the production environment as a black hole, users will not receive emails and there will be no error prompts.

How to configure Google OAuth

Optional. If not set, only email + verification code will be used to log in; if set, "Log in with Google" will appear on the login page.

Environment variables Default value Description
GOOGLE_CLIENT_ID Empty Google Cloud OAuth client ID
GOOGLE_CLIENT_SECRET Empty Google Cloud OAuth secret
GOOGLE_REDIRECT_URI http://localhost:3000/auth/callback OAuth callback address (replace self-host with your front-end domain name)

Hot-validation: The front-end gets these configurations through /api/config when it is running. There is no need to restart the front-end or rebuild the image after the change - just restart the server after the change.

For complete configuration steps (including Google Cloud Console operations), see [Login and Registration Configuration](/vibe-coding-tools/multica/auth-setup#How to configure-google-oauth).

How to configure file storage

Multica stores attachments uploaded by users (pictures, files, etc. in comments). Give priority to S3; if S3 is not available, fall back to the local disk.

S3 / S3 compatible storage

Environment variables Default value Description
S3_BUCKET Empty Only fill in the bucket name (for example my-bucket), Do not bring the .s3.<region>.amazonaws.com suffix - the server will use S3_BUCKET + S3_REGION to create the public host by itself. Enable S3 storage once set
S3_REGION us-west-2 AWS Region. Must be consistent with the region where the bucket is located - it is used for both SDK signature and public URL
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY Empty Static credentials. Use the AWS SDK default credential chain (IAM role/environment credentials) when not configured at all
AWS_ENDPOINT_URL Empty Custom S3 compatible endpoint (e.g. MinIO). If set, it will switch to path-style URL
ATTACHMENT_DOWNLOAD_MODE auto Attachment download path: auto, cloudfront, presign or proxy. When CloudFront is fully configured under auto, CloudFront will be given priority; for intranet/private endpoint hosts, server proxy will be used; for public network S3-compatible endpoints, presigned GET will be used when supported.
ATTACHMENT_DOWNLOAD_URL_TTL 30m The validity period for CloudFront signed URLs and S3 presigned download URLs. Using Go duration format

S3_BUCKET not set: The info log "S3_BUCKET not set, cloud upload disabled" will be printed when the server starts, and all uploads will fall back to the local disk.

Object Storage URL Assemble by priority:

  1. Set up CLOUDFRONT_DOMAINhttps://CLOUDFRONT_DOMAIN/<key>
  2. Set AWS_ENDPOINT_URLAWS_ENDPOINT_URL/S3_BUCKET/<key>` (path-style)
  3. AWS S3 → https://S3_BUCKET.s3.S3_REGION.amazonaws.com/<key> (virtual-hosted-style) by default. When the bucket name contains a dot, it will fall back to https://s3.S3_REGION.amazonaws.com/S3_BUCKET/<key> (path-style), because the AWS wildcard certificate cannot cover the host containing the dot.

The download_url returned by the API points to GET /api/attachments/{id}/download when CloudFront signing is not configured. This endpoint will jump to the CloudFront/S3 presigned URL when it is safe; when it encounters a private or intranet endpoint such as http://rustfs:9000, it will be streamed by the server. Docker/VPC internal object storage recommends explicitly setting ATTACHMENT_DOWNLOAD_MODE=proxy.

Local disk (when S3 is not configured)

Environment variables Default value Description
LOCAL_UPLOAD_DIR ./data/uploads Local storage directory
LOCAL_UPLOAD_BASE_URL Empty (returns relative path) Publicly accessible base URL - the full URL of the attachment cannot be obtained without a front end

CloudFront (optional)

If you use CloudFront as a CDN for S3, three relevant environment variables: CLOUDFRONT_DOMAIN, CLOUDFRONT_KEY_PAIR_ID, CLOUDFRONT_PRIVATE_KEY (or CLOUDFRONT_PRIVATE_KEY_SECRET read from Secrets Manager). Don't worry if you don't use CloudFront - it doesn't conflict with configuring S3.

Environment variables Default value Description
COOKIE_DOMAIN NULL The scope of the session cookie
  • Empty: The cookie is only effective for the host visited (single host deployment is correct)
  • Set to .example.com: the cookie is shared across all subdomains (let app.example.com and api.example.com share login status)
  • ⚠️ Cannot be an IP address (will be ignored by the browser)

How to restrict who can register

Three-tier whitelists are combined according to priority. Once the whitelist at any level is set to be non-empty, unmatched mailboxes will be rejected - even ALLOWSIGNUP=true cannot be blocked.

Environment variables Default value Description
ALLOWED_EMAILS Empty Explicit email whitelist (comma separated). If it is not empty, only the emails in the list can be registered
ALLOWED_EMAIL_DOMAINS Empty Domain name whitelist (comma separated). If it is not empty, only the domain names in the list can be registered
ALLOW_SIGNUP true Register the master switch. Set false to completely turn off registration

Unintuitive point: The combination of ALLOWED_EMAIL_DOMAINS=company.io + ALLOW_SIGNUP=true is not **"allow company.io or everyone", but only allows company.io. The AND semantics of the whitelist - the decision tree is detailed in [Login and Registration Configuration → Signup Whitelist] (/auth-setup#How to restrict who can register).

The invitation process itself does not check the signup whitelist - but the invitee must first be able to log in before they can accept the invitation. If the other party already has a Multica account (for example, registered in other workspaces), they can accept it directly without being affected by the whitelist; If the other party has not registered yet, the first step in their login (sending a verification code) will still pass the whitelist check. Emails rejected by ALLOW_SIGNUP=false or ALLOWED_EMAILS / ALLOWED_EMAIL_DOMAINS cannot complete the registration and cannot accept the invitation**.

Lock workspace creation

ALLOWSIGNUP=false can block new registrations, but it cannot prevent already logged-in users from continuing to POST /api/workspaces to open new workspaces by themselves. In self-deployed instances where you want the platform administrator to be able to see all issues/repositories/agents, turn on DISABLEWORKSPACECREATION=true to close this gap.

Environment variables Default value Description
DISABLE_WORKSPACE_CREATION false When set to true, any call to POST /api/workspaces returns 403 workspace creation is disabled for this instance. The web UI hides all workspace creation entry points through /api/config. This switch is instance-level; there is no owner or admin exception

Recommended server opening process:

  1. Start the instance with DISABLE_WORKSPACE_CREATION unset, which is the default.
  2. The administrator logs in and creates a shared workspace.
  3. Set DISABLE_WORKSPACE_CREATION=true and restart the backend. New users will then be able to join only by invitation.

If you still want the invited new users to complete the first registration, keep ALLOW_SIGNUP=true (if necessary, use ALLOWED_EMAIL_DOMAINS / ALLOWED_EMAILS to limit the registerable email addresses), and just turn on DISABLE_WORKSPACE_CREATION=true; if you set ALLOW_SIGNUP=false at the same time, even the email addresses with pending invites cannot complete the first registration, and it is only suitable for all members who already have it. Example of Multica account.

Rate limiting (optional Redis)

Public authentication endpoints, including /auth/send-code, /auth/verify-code, and /auth/google, are protected by fixed-window per-IP rate limits. The current limiter backend is Redis. When REDIS_URL is not set, the middleware passes through fail-open, and the backend logs `rate limiting disabled: REDIS_URL not configured` at startup.

Environment variables Default value Description
REDIS_URL Empty Redis connection URL (e.g. redis://localhost:6379/0). Without setting the current limit function of the authentication endpoint, it is directly turned off. The same Redis is also reused by real-time event fan-out, PAT cache, and daemon token cache; when not configured, these components fall back to memory mode/direct check DB
RATE_LIMIT_AUTH 5 Maximum number of requests per minute to /auth/send-code and /auth/google from a single IP
RATE_LIMIT_AUTH_VERIFY 20 Maximum number of requests to /auth/verify-code per minute from a single IP
RATE_LIMIT_TRUSTED_PROXIES Empty A comma-separated list of CIDRs from which source IPs are allowed to identify clients via X-Forwarded-For. Default empty = Never trust XFF, the current limiter only looks at directly connected RemoteAddr

Throttled requests will return 429 Too Many Requests, with a Retry-After: 60 header and a {"error":"too many requests"} response body.

** RATE_LIMIT_TRUSTED_PROXIES must be set when deployed behind a reverse proxy. ** Otherwise, from the backend perspective, all real users share the proxy IP, and the entire deployment falls into the same bucket, and /auth/send-code will become that the entire site can only send 5 times per minute. Common values: local Caddy / Nginx uses 127.0.0.1/32,::1/128; Cloudflare / ALB / CloudFront uses the CDN IP segment disclosed by each company. Only requests whose RemoteAddr falls within these CIDRs are allowed to rewrite the client IP via X-Forwarded-For.

Here RATE_LIMIT_TRUSTED_PROXIES and MULTICA_TRUSTED_PROXIES are not the same variable - the latter controls the rate limiter of the autopilot webhook endpoint (/api/webhooks/autopilots/{token}). The two current limiters each read their own lists, and the instance deployed behind the proxy needs to be configured with both.

Tuning parameters for the daemon process

The daemon runs on the user's local machine, and the configuration also reads local environment variables. Some commonly used ones:

Environment variables Default value Description
MULTICA_SERVER_URL ws://localhost:8080/ws server address (replace self-host with your domain name)
MULTICA_DAEMON_HEARTBEAT_INTERVAL 15s Heartbeat rate
MULTICA_DAEMON_POLL_INTERVAL 3s Task polling frequency
MULTICA_DAEMON_MAX_CONCURRENT_TASKS 20 Maximum number of concurrent tasks
MULTICA_AGENT_TIMEOUT 0 The absolute wall clock upper limit for a single task; 0 = no upper limit, the task is only subject to watchdog constraints (active tasks will not be killed because they run too long). Set another positive value when you want a hard cost/resource ceiling
MULTICA_AGENT_IDLE_WATCHDOG 30m Idle watchdog: backend continues to be silent (no messages, message queue is empty, and no tools are in transit) for so long before force-stop. 0 = disable the entire set of watchdogs
MULTICA_AGENT_TOOL_WATCHDOG 2h The upper limit of silence when the tool is in transit: If there is no output for a long time after a tool call is issued (suspected of a stuck child process), it will force-stop. 0 = close the pocket (tools in transit are never stopped)
MULTICA_PROVIDER_PATH Corresponding CLI name The executable file path of each AI programming tool (such as MULTICA_CLAUDE_PATH)
MULTICA_PROVIDER_MODEL Empty The default model of each AI programming tool
MULTICA_PROVIDER_ARGS NULL Daemon-level default CLI arguments that apply to every task in this backend, preceded by each agent's own custom_args. Supports MULTICA_CLAUDE_ARGS, MULTICA_CODEX_ARGS, MULTICA_CODEBUDDY_ARGS

For a complete explanation of the effect of each parameter on daemon behavior, see Daemons and Runtimes.

Default agent parameters (MULTICA_PROVIDER_ARGS)

Setting up a fleet-wide default CLI argument for a backend - makes it easy to apply a default cost or resource baseline (e.g. --max-turns) to all agents on a daemon without having to modify each agent's custom_args individually. This is a layer of defaults, rather than an unbreakable hard cap: each agent's own custom_args is appended, and can be overridden (see priority below).

  • Priority: The default parameters take effect first, and then each agent's own custom_args is appended. For parameters with values, the downstream CLI's own parameter parser determines the final effective value (most tools use "the latter override"), so a single agent can increase the default value of a daemon process, but where the agent does not override it, the default value still takes effect.
  • Parsing: The value is split according to POSIX shell-word rules, so quotes are available - MULTICA_CLAUDE_ARGS='--append-system-prompt "multi word"' will be parsed into two tokens.
  • Security: The default parameter layer and each agent's custom_args layer are filtered by the same set of blocked-flags, so protocol key flags (such as Claude's -p, --output-format, --input-format, --permission-mode, --mcp-config, and Codex's --listen) cannot be injected from either layer.
  • Unset/Empty means no change in behavior.

Front-end access control

Environment variables Default value Description
FRONTEND_ORIGIN Empty The front-end address. The links in the invitation email, CORS whitelist, and cookie domain are all derived from here. If the email link is not set, it will fallback to the hosted domain name https://app.multica.ai——self-host must be filled in explicitly
MULTICA_APP_URL NULL The front-end URL used by the CLI login process. The Web UI will also use it to display the self-host daemon setup command with your own app domain; in a same-origin deployment, if MULTICA_PUBLIC_URL is not set, it will also serve as the daemon's server_url
MULTICA_PUBLIC_URL Empty Public API URL without trailing slash. server_url used for autopilot webhook URL, also used as daemon by Web UI
CORS_ALLOWED_ORIGINS NULL Additional CORS allowed origins (comma separated)
ALLOWED_ORIGINS Empty WebSocket-specific origin whitelist (comma separated); if not set, fall back in the order of CORS_ALLOWED_ORIGINSFRONTEND_ORIGINlocalhost:3000/5173/5174

FRONTEND_ORIGIN will result in two silent failures: (1) The link in the invitation email points to https://app.multica.ai (the domain name of the hosted version), and the user cannot click back to your self-host instance; (2) The Origin verification of the WebSocket connection falls back to localhost:3000 / 5173 / 5174, the WebSocket deployed in production All were rejected, and the front end looked like "real-time updates are not working."

GitHub integration

GitHub PR ↔ issue integration relies on two required environment variables. Only when both are matched will enable Connect GitHub in Settings and accept webhooks. The other two optional variables are used to directly get the associated account name during installation.

Environment variables Default value Description
GITHUB_APP_SLUG Empty Your GitHub App slug (the tail of https://github.com/apps/<slug>). Settings → Use it to spell the jump URL of the install button in GitHub
GITHUB_WEBHOOK_SECRET Empty The webhook secret you set on GitHub App. Each pull_request / installation delivery uses it for HMAC-SHA256 verification; the same value is also used as the signing key for the state token in the setup callback
GITHUB_APP_ID Empty Optional. The numeric App ID on the app settings page. Use it with GITHUB_APP_PRIVATE_KEY to let the setup callback get the associated account name directly from GitHub at the moment of installation
GITHUB_APP_PRIVATE_KEY Empty Optional. The complete PEM block of the App RSA private key (containing the two lines -----BEGIN/END-----, retaining newlines). Short-lived JWT required to sign GitHub App authentication REST calls

When any required variable is not set:

  • Settings → GitHub Connect GitHub button disable, display "not configured" prompt to admin
  • /api/webhooks/github directly returns 503 github webhooks not configured - when the secret is not configured, Multica refuses to process any webhook events, instead of treating all signatures as valid

**Optional GITHUB_APP_ID / GITHUB_APP_PRIVATE_KEY When not set: **

  • After the installation is complete, the connection card will briefly display Connected to unknown. When GitHub's installation.created webhook arrives (usually within a few seconds), Multica will refresh the row to the real organization/user name, and use realtime push to update the currently open Settings → GitHub page without manual refresh.

Note: GITHUB_WEBHOOK_SECRET is also reused as the signature key of the state token in the install process, so operation and maintenance only needs to maintain one secret. It is not the Client secret of GitHub App - the Client secret is used by OAuth and has nothing to do with this integration. For the complete configuration process, see GitHub Integration → Self-Host Configuration.

Usage statistics

By default, it is reported to Multica’s official PostHog instance. If you don't want to report it, just set ANALYTICSDISABLED=true.

Environment variables Default value Description
ANALYTICS_DISABLED false Set true to completely turn off backend reporting
POSTHOG_API_KEY Built-in default key Fill in when changing to your own PostHog instance
POSTHOG_HOST https://us.i.posthog.com If you build your own PostHog, change it to your own address

Next step