Skip to content

Environment Variables

All configuration is done through environment variables, defined in your .env.prod file. This page documents every variable.

Variable Default Required Description
POSTGRES_USER breeze PostgreSQL username
POSTGRES_PASSWORD Yes PostgreSQL password. Also used to derive the unprivileged request-pool credential (see below) unless DATABASE_URL_APP or BREEZE_APP_DB_PASSWORD is set.
POSTGRES_DB breeze Database name
POSTGRES_PORT 5432 PostgreSQL port
DATABASE_URL Auto Full connection string (constructed from above in Docker)
DATABASE_URL_APP One of these three (prod) Explicit connection string for the unprivileged request role (breeze_app). Preferred, and required for multi-host / high-availability DATABASE_URL values, which cannot be derived automatically.
BREEZE_APP_DB_PASSWORD One of these three (prod) Password for the breeze_app role, used to derive its connection string from DATABASE_URL.

In production, Breeze runs request handlers as an unprivileged, row-level-security-enforced database role (breeze_app) rather than as the database owner. The API refuses to start unless it can configure that role — set one of DATABASE_URL_APP, BREEZE_APP_DB_PASSWORD, or POSTGRES_PASSWORD. This check runs whenever NODE_ENV=production, including when AUTO_MIGRATE=false. The API also refuses to start if it detects it is running as a SUPERUSER or BYPASSRLS role — if you were previously running as the postgres superuser, switch to a NOSUPERUSER/NOBYPASSRLS role. After doing so you may notice rows that were previously visible to a tenant “disappear”; that is row-level security working correctly, not data loss. See the request-database-role runbook.

Variable Default Required Description
REDIS_URL redis://localhost:6379 Yes (prod) Redis connection URL. Must carry a password in production (e.g., redis://:password@localhost:6379) — the API refuses to boot if it doesn’t.
REDIS_PORT 6379 Redis port
REDIS_PASSWORD Yes (prod) Password for Redis authentication. Set in docker-compose and include in REDIS_URL.
REDIS_PASSWORD_FILE Path to a file containing the Redis password (Docker secret style). Used in place of REDIS_PASSWORD when secrets are mounted as files.
Variable Default Required Description
JWT_SECRET Yes JWT signing key (min 32 chars). Generate: openssl rand -base64 64
AGENT_ENROLLMENT_SECRET Yes Shared secret for agent enrollment. Generate: openssl rand -hex 32
APP_ENCRYPTION_KEY Yes AES encryption key for sensitive data at rest
MFA_ENCRYPTION_KEY Yes Encryption key for MFA secrets
ENROLLMENT_KEY_PEPPER Yes HMAC pepper for enrollment key hashing
MFA_RECOVERY_CODE_PEPPER Yes HMAC pepper for recovery code hashing
ENROLLMENT_KEY_DEFAULT_TTL_MINUTES 60 Default enrollment key expiry
ENROLLMENT_KEY_CLEANUP_ENABLED true Set to false to disable the daily sweep that purges expired enrollment keys
ENROLLMENT_KEY_PURGE_AFTER_DAYS 7 Grace period (days past expiry) before an expired enrollment key is purged
SESSION_SECRET Yes Express session signing secret
SESSION_MAX_AGE 86400000 Session max age in ms (24h)

Access-token (15m) and refresh-token (7d) lifetimes are fixed in code (apps/api/src/services/jwt.ts) — short-lived access tokens backed by refresh-token rotation — and are intentionally not configurable via deployment env vars.

Variable Default Required Description
NODE_ENV production Environment mode
API_PORT 3001 API server port
WEB_PORT 4321 Web dashboard port
PUBLIC_API_URL https://${BREEZE_DOMAIN} (bundled compose only) Public API URL used for generated agent installers and shareable links. The bundled docker-compose.yml derives this from BREEZE_DOMAIN. If you use a custom compose file, you must set PUBLIC_API_URL in .env and map it into the api service environment: block, or Generate Link / Download Installer will fail with Server URL not configured.
API_URL Legacy fallback for PUBLIC_API_URL, honored only by enrollment, installer, and MCP-invite code paths. Auto-update, dev-push, and a few other routes read PUBLIC_API_URL only — prefer setting PUBLIC_API_URL.
BREEZE_DOMAIN Yes (prod) Domain for Caddy TLS provisioning
ACME_EMAIL Yes (prod) Email for Let’s Encrypt certificate notifications
CORS_ALLOWED_ORIGINS Comma-separated allowed CORS origins
IS_HOSTED Yes (prod) true for hosted SaaS edition, false for self-hosted. Must be set explicitly — the API refuses to boot otherwise. Controls signup gating, billing, and email-verification policy.
TRUST_PROXY_HEADERS Yes (prod) true when behind a reverse proxy (Caddy, Cloudflare). Must be set explicitly in production.
TRUSTED_PROXY_CIDRS When TRUST_PROXY_HEADERS=true Comma-separated CIDRs of trusted reverse proxies (e.g., 10.0.0.0/8,172.16.0.0/12). Required when proxy headers are trusted.
TRUST_CF_CONNECTING_IP false Set to true only when Breeze is fronted by Cloudflare. Cloudflare overwrites the CF-Connecting-IP header at its edge, but a non-Cloudflare reverse proxy (including the bundled Caddy) does not strip it — so trusting it without a Cloudflare front would let a client spoof the header to defeat IP allowlists and per-IP rate limits. When off, client IPs are resolved from X-Forwarded-For instead. If you are behind Cloudflare and leave this unset, client-IP attribution (rate limits, audit logs, IP allowlists) falls back to X-Forwarded-For; the API logs a startup warning in that case.
IP_ALLOWLIST_ENFORCEMENT_MODE enforce Partner dashboard IP allowlist mode. Use off only as a break-glass switch if an allowlist locks everyone out. The allowlist only enforces when a partner has entries configured and proxy trust is working through TRUST_PROXY_HEADERS plus TRUSTED_PROXY_CIDRS.
DASHBOARD_URL URL for links in emails
PUBLIC_APP_URL Public-facing app URL
PUBLIC_PORTAL_URL https://${BREEZE_DOMAIN}/portal (bundled compose only) Public origin for customer-portal links in outbound email (e.g. invoice emails). Falls back to PUBLIC_APP_URL if unset. The portal is served under /portal on the main domain.
PORTAL_BASE_PATH /portal Base path the customer portal is served under. Baked into the portal image at build time — changing it requires rebuilding the portal image and keeping the Caddy /portal route in sync.
Variable Default Description
EMAIL_PROVIDER auto Provider: auto, resend, smtp, or mailgun
RESEND_API_KEY Resend API key
EMAIL_FROM noreply@breeze.local Sender address
SMTP_HOST SMTP server hostname
SMTP_PORT 587 SMTP port
SMTP_USER SMTP username
SMTP_PASS SMTP password
SMTP_FROM noreply@breeze.local SMTP-specific sender address
SMTP_SECURE false Use TLS for SMTP
MAILGUN_API_KEY Mailgun API key
MAILGUN_DOMAIN Mailgun sending domain
MAILGUN_BASE_URL https://api.mailgun.net Mailgun API base URL
MAILGUN_FROM noreply@breeze.local Mailgun-specific sender address
Variable Default Description
TWILIO_ACCOUNT_SID Twilio Account SID
TWILIO_AUTH_TOKEN Twilio Auth Token
TWILIO_VERIFY_SERVICE_SID Twilio Verify service SID (for SMS MFA)
TWILIO_MESSAGING_SERVICE_SID Twilio Messaging Service SID (for alert SMS)
TWILIO_PHONE_NUMBER Twilio phone number for outbound SMS

Push notifications to the iOS app go directly through Apple’s push service (APNs). All four values are optional but all-or-none — setting any one makes the other three required, so a half-configured setup fails at startup rather than silently at first send. Leave them all unset to disable push.

Variable Default Description
APNS_AUTH_KEY Contents of the Apple .p8 push auth key
APNS_KEY_ID Apple push key ID
APNS_TEAM_ID Apple developer team ID
APNS_BUNDLE_ID iOS app bundle identifier
APNS_ENVIRONMENT production production or sandbox. An empty value is treated as unset.
Variable Default Required Description
BINARY_SOURCE local Download source: local (serve from disk, optional S3) or github (redirect to GitHub Releases)
AGENT_BINARY_DIR ./agent/bin Local directory containing agent binaries
VIEWER_BINARY_DIR ./viewer/bin Local directory containing viewer installers
HELPER_BINARY_DIR /data/binaries/helper Local directory containing helper binaries
BINARY_VERSION_FILE Path to VERSION file for local mode DB registration (set automatically in Docker Compose)
BINARY_VERSION Release tag for GitHub redirect mode (falls back to BREEZE_VERSION, then latest)
RELEASE_ARTIFACT_MANIFEST_PUBLIC_KEYS yzx8ftmcls6uBetFC5SYnZhBo+cbur3IX50TbBthTso= (official releases) Yes (prod) Comma-separated raw base64 Ed25519 public keys that sign release manifests. Use the published default for official Breeze releases (it is a public key, safe to commit); only change it if you sign your own binaries. The API refuses to start in production without it. BREEZE_RELEASE_ARTIFACT_MANIFEST_PUBLIC_KEYS is accepted as an alias.
AGENT_AUTO_PROMOTE true Controls whether a newly registered/synced agent release immediately becomes the fleet upgrade target. true (default): publishing a release promotes it to the fleet at once. false: new binaries stay downloadable but the fleet only advances when you explicitly promote a version (platform admin + MFA) — see Controlled agent fleet rollout.

See Binary Distribution for details on local vs GitHub mode and S3 offloading.

Variable Default Description
S3_ENDPOINT S3-compatible endpoint (MinIO, R2, AWS). Uses path-style addressing.
S3_ACCESS_KEY Access key
S3_SECRET_KEY Secret key
S3_BUCKET Bucket name
S3_REGION us-east-1 Bucket region
S3_PRESIGN_TTL 900 Presigned URL expiration in seconds (15 min)
MINIO_API_PORT 9000 MinIO API port (Docker only)
MINIO_CONSOLE_PORT 9001 MinIO web console port (Docker only)

The Breeze stack includes a coturn TURN server for WebRTC relay. Without TURN, remote desktop connections fail when either peer is behind symmetric NAT or a restrictive firewall.

Variable Default Required Description
TURN_HOST Yes (prod) Public IP or hostname of the TURN server. Must be reachable by agents and viewers.
TURN_PORT 3478 TURN listening port (UDP and TCP)
TURN_SECRET Yes (prod) Shared secret for TURN credential generation. Generate: openssl rand -hex 32
TURN_REALM breeze.local TURN authentication realm
Variable Default Description
METRICS_SCRAPE_TOKEN Bearer token for /metrics/scrape
METRICS_INCLUDE_ORG_ID false Include org IDs in Prometheus labels
METRICS_SCRAPE_IP_ALLOWLIST Restrict metrics scraping by IP
LOG_LEVEL info Log verbosity: debug, info, warn, error
LOG_JSON true Structured JSON logging
GRAFANA_ADMIN_USER admin Grafana admin username
GRAFANA_ADMIN_PASSWORD Grafana admin password
GRAFANA_PORT 3000 Grafana web UI port (monitoring stack)
GRAFANA_ROOT_URL http://localhost:3000 Grafana public root URL (monitoring stack)
PROMETHEUS_PORT 9090 Prometheus web UI port (monitoring stack)
ALERTMANAGER_PORT 9093 Alertmanager web UI port (monitoring stack)
LOKI_PORT 3100 Loki log aggregation API port (monitoring stack)

Both API and web Sentry integrations are off by default. Leave the DSN variables blank to disable. See Error Tracking & Privacy for what gets collected and how scrubbing works.

Variable Default Description
SENTRY_DSN API Sentry DSN. Leave blank to disable server-side error tracking.
SENTRY_ENVIRONMENT production Sentry environment tag
SENTRY_RELEASE Sentry release tag (e.g. git SHA)
SENTRY_TRACES_SAMPLE_RATE 0.1 Sentry performance trace sample rate (0.0-1.0)
SENTRY_PROFILES_SAMPLE_RATE API performance-profiling sample rate (0.0-1.0). Leave blank to disable profiling.
PUBLIC_SENTRY_DSN_WEB Web Sentry DSN. Leave blank to disable browser error tracking and on-error session replay. Inlined into the web bundle at build time.
SENTRY_AUTH_TOKEN Sentry auth token used during the web build to upload source maps. If unset, source map upload is skipped and the build still succeeds.

Two rate-limit tiers protect the API. The generic per-user limit covers logged-in dashboard usage; the per-org agent limit caps how much traffic a single tenant’s fleet can generate, even with thousands of agents.

Variable Default Description
RATE_LIMIT_WINDOW_MS 60000 Sliding window duration (ms)
RATE_LIMIT_MAX_REQUESTS 100 Max requests per window
AGENT_ORG_RATE_LIMIT_PER_MIN 600 Per-organization sliding-window rate limit on agent-authenticated endpoints. Returns 429 with Retry-After: 60 when exceeded. Sized for ~5 active agents per org; raise for MSPs with very large fleets.
Variable Default Description
DB_POOL_MAX 30 Maximum postgres-js connection pool size. Tune up if you see cascading 504s during heartbeat storms; confirm Postgres max_connections has headroom (default 100 is fine for a single API replica).
Variable Default Description
MAX_ACTIVE_REMOTE_SESSIONS_PER_ORG 10 Concurrent remote sessions per org
MAX_ACTIVE_REMOTE_SESSIONS_PER_USER 5 Concurrent remote sessions per user
PATCH_REPORT_STORAGE_PATH ./data/patch-reports Patch compliance report storage
Variable Default Description
ENABLE_REGISTRATION true Allow new user registration
ENABLE_2FA true Enable two-factor authentication
ENABLE_API_DOCS false Enable Swagger API documentation
ENABLE_API_DOCS_UI false Enable interactive Swagger UI (requires ENABLE_API_DOCS=true)
USE_AGENT_SDK Use Claude Agent SDK for AI chat
PORTAL_STATE_BACKEND memory Portal state backend: memory or redis (auto redis in production)
PUBLIC_ENABLE_EDR_INTEGRATIONS false Surface the EDR UI: the /security/edr fleet page, the device Security-tab EDR panel, and the Security dashboard summary cards. Set true at web image build time to enable.
PUBLIC_ENABLE_NETWORK_DEVICES_IN_LIST false Surface network-discovered assets alongside agent endpoints in the Devices list view. Set true at web image build time to enable.
Variable Default Description
MCP_SSE_RATE_LIMIT_PER_MINUTE 30 SSE connection rate limit per API key
MCP_MESSAGE_RATE_LIMIT_PER_MINUTE 120 Message rate limit per API key
MCP_MAX_SSE_SESSIONS_PER_KEY 5 Max concurrent SSE sessions per API key
MCP_REQUIRE_EXECUTE_ADMIN false Require ai:execute_admin scope for Tier 3 tools
MCP_EXECUTE_TOOL_ALLOWLIST Comma-separated allowed Tier 3 tools (empty = deny all)
Variable Default Description
CLOUDFLARE_API_TOKEN Cloudflare API token with Client Certificates permission
CLOUDFLARE_ZONE_ID Cloudflare zone ID for your domain
Variable Default Description
CF_ACCESS_TRUST_ENABLED false Set true to short-circuit POST /auth/login when a valid Cf-Access-Jwt-Assertion header is present. Off by default. See Cloudflare Access trust.
CF_ACCESS_TEAM_DOMAIN Required when trust is enabled. Bare hostname of your Cloudflare team domain, e.g. example.cloudflareaccess.com. No scheme.
CF_ACCESS_AUD Required when trust is enabled. AUD tag for the Cloudflare Access application protecting Breeze.
CF_ACCESS_TRUSTS_MFA false Treat a valid CF Access JWT as MFA-satisfied for the minted Breeze session. Only enable if your CF Access policy actually requires step-up MFA.
Variable Default Description
C2C_M365_CLIENT_ID Azure AD app (client) ID for multi-tenant M365 backup. When set with the secret below, enables one-click admin consent flow for connecting Microsoft 365 backups.
C2C_M365_CLIENT_SECRET Azure AD app client secret for multi-tenant M365 backup

These enable partners to connect a Microsoft 365 shared support mailbox so customer email becomes tickets and replies are sent from that mailbox. This is a separate Azure app registration from the Cloud-to-Cloud Backup app above.

Variable Default Description
TICKET_MAILBOX_M365_CLIENT_ID Azure AD app (client) ID for the Breeze Ticketing app. When set with the secret, the Microsoft 365 mailbox card appears under Settings → Ticketing → Inbound Email.
TICKET_MAILBOX_M365_CLIENT_SECRET Client secret for the Breeze Ticketing Azure app.
PUBLIC_TICKET_MAILBOX_APP_ID Build-time, browser-exposed app id shown in the Application Access Policy PowerShell snippet on the settings card. Safe to publish – it is the app’s public client id.
Variable Default Description
BREEZE_VERSION latest Breeze release tag for Docker images (e.g. 0.50.0). Also used by GitHub redirect mode for agent binary downloads.
DOCKER_PLATFORM linux/amd64 Container platform. GHCR images are amd64 only. On Apple Silicon, use docker-compose.override.yml.local-build to build native arm64 images instead.
REDIS_MAXMEMORY 256mb Maximum memory Redis is allowed to use. Redis runs with noeviction policy so BullMQ jobs are never silently dropped.
Variable Default Description
MSI_SIGNING_URL URL of the remote Windows signing service. When set, pre-configured MSI installer downloads are code-signed before delivery.
MSI_SIGNING_CF_ACCESS_ID Cloudflare Access service token Client ID for authenticating to the signing service
MSI_SIGNING_CF_ACCESS_SECRET Cloudflare Access service token Client Secret for authenticating to the signing service
Variable Default Description
BILLING_SERVICE_URL URL of the billing service for AI credit checks and deductions. When unset, AI usage is unlimited.
BILLING_SERVICE_API_KEY API key for authenticating to the billing service
Variable Default Description
ANTHROPIC_API_KEY Anthropic API key for AI assistant (BYOK)
ANTHROPIC_BASE_URL Self-hosted only. Point the AI Agent at an Anthropic-compatible /v1/messages backend (vLLM 0.23+, LiteLLM). Must be an http(s) URL. Permitted only when IS_HOSTED is explicitly false (or 0/no/off); refused at boot if IS_HOSTED is true, unset, or invalid.
ANTHROPIC_AUTH_TOKEN Bearer token for the custom backend (use instead of ANTHROPIC_API_KEY when ANTHROPIC_BASE_URL is set).
ANTHROPIC_MODEL claude-sonnet-4-6 Override the default model id for a raw vLLM backend. Leave unset with a LiteLLM gateway that aliases claude-sonnet-4-6 → your model.

Self-hosted: alternative Anthropic-compatible backends

Section titled “Self-hosted: alternative Anthropic-compatible backends”

A self-hosted operator can run the AI Agent against any backend that speaks the Anthropic /v1/messages dialect — keeping full tool-use and the aiGuardrails stack — by setting:

Terminal window
IS_HOSTED=false # required — ANTHROPIC_BASE_URL is refused otherwise
ANTHROPIC_BASE_URL=http://localhost:8000 # your vLLM / LiteLLM endpoint
ANTHROPIC_AUTH_TOKEN=<backend bearer token>
# Raw vLLM only — match the served model id (skip with a LiteLLM alias):
ANTHROPIC_MODEL=<served-model-id>

The recommended route is a LiteLLM gateway that aliases claude-sonnet-4-6 to your backend model, so no ANTHROPIC_MODEL override is needed. For an OpenAI-dialect-only endpoint, use the separate MCP_LLM_* openai-compatible path instead (chat-only, no tool-use).

ANTHROPIC_BASE_URL is fail-closed: it is honored only when self-host is affirmatively declared (IS_HOSTED explicitly false/0/no/off). If IS_HOSTED is true, unset, or set to an unrecognized value, the API refuses to boot when ANTHROPIC_BASE_URL is present, and the value is also stripped from the SDK subprocess environment — so a stray or unmapped IS_HOSTED can never redirect platform AI traffic to a third-party endpoint.

Cost tracking is best-effort. The SDK can’t price a non-Anthropic model id, so it reports total_cost_usd: 0; Breeze then falls back to token-based pricing, and an unrecognized model id is priced at conservative Opus-tier DEFAULT_PRICING ($5/$25 per MTok) — i.e. likely an over-estimate for a cheap local model, not $0. For accurate accounting, add the served model id to MODEL_PRICING in aiCostTracker.ts, or use the openai-compatible path’s MCP_LLM_PRICE_* overrides.

Variable Default Description
QBO_CLIENT_ID Intuit OAuth client ID for the QuickBooks Online integration
QBO_CLIENT_SECRET Intuit OAuth client secret
QBO_REDIRECT_URI OAuth callback URL registered with Intuit (e.g. https://your-domain/accounting/quickbooks/callback)
QBO_ENVIRONMENT sandbox or production
Variable Default Description
NVD_API_KEY Optional NIST NVD API key. Raises NVD request rate limits so vulnerability feed syncs run faster. Without it, syncs use the slower public rate; the feature works either way.