Environment Variables

Every configuration value.


Every setting QQueue reads from your .env file, explained in plain language. You don't need to memorize any of this: running pnpm setup walks you through the values that matter and generates the secret ones for you. This page is the reference for when you want to know what a setting actually does.

Settings live in one file, .env, at the root of your QQueue folder. Back this file up somewhere safe (a password manager works well) — it contains keys that cannot be recreated later.

Rule of thumb: for local development the defaults work as-is. For production, pnpm setup --mode=production --domain=your.domain fills in everything that must change.


The two values you must never change after you start sending

VariableWhat it isWhy changing it hurts
ENCRYPTION_KEYThe key that locks the sending-account (SMTP) passwords you save inside QQueue.If it is lost, QQueue can no longer unlock any saved sending passwords and you must re-enter every one. It can be rotated safely: set ENCRYPTION_KEYS=new-key,old-key (new first), restart, run pnpm rotate-secrets, then drop the old key.
TRACKING_SECRETA random password QQueue uses to sign the links that track email opens and clicks.If it changes, tracking in emails you've already sent stops working — those links are signed with the old secret.

Both are generated for you by pnpm setup. Treat them like the keys to the building.

Security keys

VariableWhat it is
JWT_ACCESS_SECRETSigns the short-lived login sessions for the dashboard.
JWT_REFRESH_SECRETSigns the longer-lived "keep me signed in" tokens.
ENCRYPTION_KEYSee above — encrypts saved SMTP passwords at rest.
ENCRYPTION_KEYSOptional. A comma-separated keyring for key rotation: the first key encrypts, every key can decrypt. Overrides ENCRYPTION_KEY when set.
TRACKING_SECRETSee above — signs open/click tracking links.
INBOUND_ESP_WEBHOOK_ENABLEDOptional, default false. Turns on the inbound ESP webhook endpoint (POST /api/v1/webhooks/email-events). Leave it off unless you relay through an email provider that posts bounce reports to QQueue — QQueue detects bounces on its own for normal SMTP sending (rejected sends and bounce emails read from a synced inbox).
WEBHOOK_SECRETOptional. The shared password an email provider must send with each report when INBOUND_ESP_WEBHOOK_ENABLED=true. With the endpoint enabled but no secret set, every request is rejected.
TRUST_PROXYOptional, default 1. How many reverse proxies sit in front of the API. The standard deployment (Caddy) is 1. Set 0 if the API faces the internet directly, or 2 if you added your own proxy (like Nginx) in front of Caddy. If this is wrong, rate limiting counts all visitors as one.
DEV_ECHO_RESET_TOKENOptional, default false. For local development only: shows the password-reset token in the API response so you can reset a password without a working sending account. Never turn this on for a real instance — it would let anyone reset anyone's password.

Mailcow provisioning (optional)

Lets owners and admins create team mailboxes from QQueue's Mailboxes page instead of Mailcow's admin UI. QQueue creates the mailbox, keeps an app password for sending, and connects the inbox so bounces are tracked automatically. Leave these unset if you manage mailboxes yourself.

VariableWhat it is
MAILCOW_API_URLThe address of your Mailcow server (e.g. https://mail.example.com).
MAILCOW_API_KEYAn API key from Mailcow's Admin → API page. It needs read/write access.
MAILCOW_MAIL_HOSTOptional. Where provisioned mailboxes send and receive (SMTP/IMAP). Defaults to the MAILCOW_API_URL hostname.
MAILCOW_SMTP_PORT / MAILCOW_IMAP_PORTOptional, default 465 / 993 (both TLS).

All of these are random strings — no human ever needs to read or remember them. Generate any of them by hand with openssl rand -hex 32 if you're not using pnpm setup.

Core

VariableDefaultWhat it is
NODE_ENVdevelopmentTells QQueue whether it's running in development or production. pnpm setup --mode=production sets this for you.
API_PORT4000The port the API listens on.

Domain and URLs

VariableDefaultWhat it is
DOMAINmail.example.comProduction only: the web address of your QQueue server. The production Docker stack derives the URLs below from it and requests an HTTPS certificate for it. Point your DNS at the server before going live.
APP_URLhttp://localhost:4000The public address where open/click tracking links resolve. It appears inside the emails you send, so in production it must be your real HTTPS domain — otherwise recipients' clicks go nowhere and tracking silently fails.
PUBLIC_APP_URLhttp://localhost:5173The public address of the dashboard, used to build user-facing links such as password resets.
WEB_ORIGIN(unset)Production only: the origin the API accepts browser requests from (CORS). The production stack derives it from DOMAIN.

⚠ Changing APP_URL/DOMAIN after you've sent email breaks the tracking links in those already-sent emails. Pick the address you plan to keep.

Database and queue

VariableDefaultWhat it is
DATABASE_URLlocal PostgresWhere QQueue stores everything: contacts, emails, templates, settings. The default points at the Postgres container from docker compose up -d. Using a hosted database instead (e.g. Neon)? See the managed infrastructure guide.
POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DBqqueue / generated / qqueueCredentials for the bundled production Postgres container. pnpm setup --mode=production generates the password; the stack assembles its own connection string from these.
PROD_DATABASE_URL(blank)Production override: set this to use an external/hosted Postgres instead of the bundled container. Leave blank to use the bundled one.
REDIS_HOST / REDIS_PORTlocalhost / 6379Redis is the queue QQueue uses to send email in the background, retry failures, and run campaigns. Local default is the bundled container; hosted options in the managed infrastructure guide.
REDIS_PASSWORD / REDIS_TLS(blank) / falseOnly needed for hosted Redis (e.g. Upstash): its password and TLS switch. The bundled private container needs neither.
PROD_REDIS_HOST / PROD_REDIS_PORT(blank)Production override for an external/hosted Redis. Leave blank to use the bundled one.

File storage (attachments)

QQueue stores email attachments in S3-compatible object storage. Locally and in the bundled production stack that's MinIO — you don't have to set anything up. To use a hosted service (Cloudflare R2, AWS S3), see the managed infrastructure guide.

VariableDefaultWhat it is
S3_ENDPOINThttp://localhost:9100Where the storage service lives. For AWS S3, set this empty.
S3_REGIONus-east-1Storage region.
S3_BUCKETqqueue-attachmentsThe bucket attachments are stored in (created automatically on boot).
S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEYqqueue / qqueue-secretStorage credentials. For the bundled MinIO, S3_SECRET_ACCESS_KEY must equal MINIO_ROOT_PASSWORDpnpm setup keeps them in sync.
S3_FORCE_PATH_STYLEtrueKeep true for MinIO and most self-hosted storage; set false for AWS S3.
MINIO_ROOT_PASSWORDgeneratedRoot password for the bundled MinIO container (production stack).
ATTACHMENT_MAX_BYTES10485760 (10 MB)The largest attachment a single email may carry.

Deliverability tuning

Sensible defaults — leave these alone unless you know you need different. The API and worker read the same .env, so they always agree.

VariableDefaultWhat it is
SOFT_BOUNCE_THRESHOLD3How many temporary ("soft") bounces an address may have inside the window before QQueue stops sending to it. Hard bounces and spam complaints always suppress immediately.
SOFT_BOUNCE_WINDOW_DAYS30The window for counting soft bounces.
DEFAULT_DOMAIN_MAX_PER_MINUTE60To protect your reputation with mail providers, QQueue limits how many emails per minute it sends to any single recipient domain (all @gmail.com addresses count together). Organizations can override this per domain in the dashboard.

Inbox sync

VariableDefaultWhat it is
INBOX_SYNC_INTERVAL_SECONDS120How often the worker checks connected IMAP inboxes for replies.
INBOX_SYNC_MAX_MESSAGES50On an inbox's first sync, only the latest N messages are imported.

Settings that live in the dashboard instead

Not everything is an environment variable. These are configured in the app (Settings → Instance, instance administrators only) and stored in the database:

  • Allow public registration — whether visitors can create accounts at /register, chosen during the first-run setup wizard and changeable any time.

Per-organization operational settings (sending accounts, suppression policy, per-domain throttles, webhooks, API keys) also live in the dashboard, not in .env.

  • Managed infrastructure — hosted Postgres, Redis, and storage (Neon, Upstash, R2) instead of the bundled containers.
  • Quickstart — local development from clone to first email.
  • Deploy — production VPS deployment.
  • Troubleshooting — when something doesn't work.