-
-
Notifications
You must be signed in to change notification settings - Fork 0
Configuration Reference
Mailbridge is configured primarily through environment variables in .env, with additional Cloudflare Worker bindings and secrets configured through wrangler.toml and Wrangler secrets.
This reference assumes the deployment model described in Quick Start:
- Inbound mail is received by Cloudflare Email Routing.
- The Cloudflare Worker encrypts inbound mail and stores it temporarily in R2.
- Cloudflare Queues deliver references to the Mailbridge webhook.
- Mailbridge decrypts, scans, and forwards inbound mail to a local SMTP server.
- Trusted local systems can submit outbound mail to the Mailbridge SMTP relay.
- The SMTP relay is unauthenticated but restricted by allowed source CIDRs.
- Outbound relay delivery uses Cloudflare Email Service through the Worker.
# Node App Configuration
PORT=3090
SMTP_RELAY_PORT=2525
SMTP_RELAY_SOCKET_TIMEOUT_MS=120000
SMTP_RELAY_MAX_MESSAGE_BYTES=52428800
MAILBRIDGE_VERBOSE_LOGGING=true
MAILBRIDGE_HOSTNAME=mailbridge.example.com
QUEUE_MAX_ATTEMPTS=20
DATA_DIR=/app/data
SECRETS_DB_PATH=/app/secrets/secrets.db
QUEUE_MASTER_KEY=replace_with_generated_base64_key
MAILBRIDGE_PRIVATE_KEY_PATH=/app/secrets/mailbridge-r2-private.pem
AUDIT_LOG_RETENTION_DAYS=1
# Optional in-container Cloudflare Tunnel
CLOUDFLARED_ENABLED=false
CLOUDFLARED_TUNNEL_TOKEN=
CLOUDFLARED_LOGLEVEL=info
# Worker -> Mailbridge webhook authentication
WEBHOOK_SECRET=replace_with_generated_shared_secret
# Local Mail Server Configuration
LOCAL_MAIL_HOST=mail.internal.example
LOCAL_MAIL_PORT=25
LOCAL_MAIL_SECURE=false
LOCAL_MAIL_REQUIRE_TLS=false
LOCAL_MAIL_TLS_REJECT_UNAUTHORIZED=false
LOCAL_MAIL_TLS_SERVERNAME=
LOCAL_MAIL_TLS_CA_FILE=
# Outbound Relay Provider
RELAY_UPSTREAM_PROVIDER=cloudflare
RELAY_API_KEY=
RELAY_FROM_FALLBACK=postmaster@example.com
RESEND_BASE_URL=https://api.resend.com
MAILGUN_DOMAIN=
MAILGUN_BASE_URL=https://api.mailgun.net
CLOUDFLARE_SEND_WORKER_URL=https://mailbridge-worker.example.workers.dev/api/send/email
# Dedicated Mailbridge -> Worker outbound authentication
CLOUDFLARE_SEND_WEBHOOK_SECRET=replace_with_separate_outbound_secret
# Unauthenticated SMTP Relay
SMTP_RELAY_ENABLED=true
SMTP_RELAY_VERBOSE_LOGGING=true
SMTP_RELAY_INJECT_HEADERS=true
SMTP_RELAY_REQUIRE_TLS=false
SMTP_RELAY_ALLOW_INSECURE=true
SMTP_RELAY_ALLOWED_CIDRS=192.168.1.0/24,127.0.0.1/32,::1/128
SMTP_RELAY_TLS_CERT_FILE=
SMTP_RELAY_TLS_KEY_FILE=
SMTP_RELAY_TLS_CA_FILE=
# SpamAssassin
SPAMASSASSIN_MODE=local
POSTMARK_SPAMCHECK_URL=https://spamcheck.postmarkapp.com/filter
SPAMD_HOST=127.0.0.1
SPAMD_PORT=783
SPAMD_STARTUP_ATTEMPTS=30
SPAMC_TIMEOUT_MS=10000
SPAMC_FAIL_OPEN=false
SA_BLOCK_THRESHOLD=12
SA_QUESTIONABLE_THRESHOLD=5
SPAM_SCL_SCORE=9
SPAM_SUBJECT_TAG=[SPAM]
# Optional Spamhaus Intelligence API Checks
SPAMHAUS_ENABLED=false
SPAMHAUS_USERNAME=
SPAMHAUS_PASSWORD=
SPAMHAUS_FAIL_OPEN=true
# Optional AI Secondary Screening
AI_ENABLED=false
AI_API_KEY=
AI_MODEL=gpt-5.4-nano
AI_BASE_URL=
AI_INPUT_SCOPE=headers
AI_MAX_INPUT_CHARS=20000These settings control the Mailbridge Node.js service, local storage, retry behavior, and operational logging.
| Variable | Required | Default | Description |
|---|---|---|---|
PORT |
No | 3090 |
HTTP port used by the Mailbridge webhook and health endpoint. |
SMTP_RELAY_PORT |
No | 2525 |
SMTP listener port used when outbound relay is enabled. |
SMTP_RELAY_SOCKET_TIMEOUT_MS |
No | 120000 |
Maximum SMTP relay socket inactivity timeout in milliseconds. |
MAILBRIDGE_VERBOSE_LOGGING |
No | true |
Enables verbose application logging. Accepted true values include true, 1, yes, and on. |
MAILBRIDGE_HOSTNAME |
No | mailbridge.example.com |
Hostname used when identifying the Mailbridge relay in generated outbound metadata and logs. |
QUEUE_MAX_ATTEMPTS |
No | 20 |
Maximum number of local retry attempts before a queued delivery is no longer retried. |
DATA_DIR |
No | Application data directory |
Directory containing audit data and encrypted retry queue files. Docker deployments should use /app/data. |
SECRETS_DB_PATH |
No | Application secrets/secrets.db
|
SQLite path containing active queue secret material. Docker deployments should use /app/secrets/secrets.db. |
QUEUE_MASTER_KEY |
Yes | None | Base64 master key used to protect locally queued retry messages. Generate once per deployment and protect it as a secret. |
MAILBRIDGE_PRIVATE_KEY_PATH |
Required for encrypted inbound R2 delivery | None | Path to the RSA private key Mailbridge uses to decrypt Worker-encrypted inbound mail. |
AUDIT_LOG_RETENTION_DAYS |
No |
1 in example config |
Number of days to retain audit records. |
With the recommended Docker volume mounts:
data/
├── mailbridge.db
└── queue/
└── <queued-message-id>.eml
secrets/
├── secrets.db
└── mailbridge-r2-private.pem
Purpose of each item:
| Path | Purpose | Sensitivity |
|---|---|---|
data/mailbridge.db |
Audit-only SQLite database. | Sensitive operational metadata. |
data/queue/*.eml |
Encrypted retry queue items created for temporary delivery failures. | Encrypted but still sensitive. |
secrets/secrets.db |
Queue secret material used to access queued messages. | Highly sensitive. |
secrets/mailbridge-r2-private.pem |
Private key used to decrypt inbound mail encrypted by the Worker. | Critical secret. |
mkdir -p data/queue secrets
export QUEUE_MASTER_KEY="$(openssl rand -base64 32 | tr -d '\n')"
export WEBHOOK_SECRET="$(openssl rand -base64 48 | tr -d '\n')"
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
-out secrets/mailbridge-r2-private.pem
openssl rsa -pubout \
-in secrets/mailbridge-r2-private.pem \
-out secrets/mailbridge-r2-public.pem
chmod 600 secrets/mailbridge-r2-private.pemDo not commit generated keys, .env, database files, or queue contents.
Mailbridge can optionally launch cloudflared inside the same container. This is useful when the container itself should expose the webhook endpoint publicly without directly publishing port 3090 to the internet.
| Variable | Required | Default | Description |
|---|---|---|---|
CLOUDFLARED_ENABLED |
No | false |
Starts cloudflared inside the container when enabled. |
CLOUDFLARED_TUNNEL_TOKEN |
When enabled | None | Cloudflare Tunnel token used by the in-container connector. |
CLOUDFLARED_LOGLEVEL |
No | info |
Cloudflared logging level. |
CLOUDFLARED_ENABLED=true
CLOUDFLARED_TUNNEL_TOKEN=replace_with_cloudflare_tunnel_token
CLOUDFLARED_LOGLEVEL=infoThe tunnel public hostname should route to:
http://localhost:3090
When cloudflared runs outside the Mailbridge container:
CLOUDFLARED_ENABLED=false
CLOUDFLARED_TUNNEL_TOKEN=Do not configure two tunnel connectors for the same hostname unless that is intentional.
Inbound webhook requests from the Cloudflare Worker to Mailbridge must provide the configured shared secret.
| Variable | Required | Default | Description |
|---|---|---|---|
WEBHOOK_SECRET |
Yes | None | Shared secret used to validate incoming requests to /api/webhook/email. |
Mailbridge expects the Worker to send:
X-Webhook-Secret: <WEBHOOK_SECRET>The same secret must be configured in both places:
WEBHOOK_SECRET=replace_with_generated_shared_secretprintf '%s' "$WEBHOOK_SECRET" | \
npx wrangler secret put WEBHOOK_SECRET --name "$WORKER_NAME"The Worker validates /api/send/email against the dedicated CLOUDFLARE_SEND_WEBHOOK_SECRET when it is configured. It falls back to WEBHOOK_SECRET only for compatibility.
Mailbridge sends its outbound Worker request using:
CLOUDFLARE_SEND_WEBHOOK_SECRET, when configured
WEBHOOK_SECRET, when CLOUDFLARE_SEND_WEBHOOK_SECRET is empty
Recommended configuration:
WEBHOOK_SECRET=replace_with_generated_shared_secret
CLOUDFLARE_SEND_WEBHOOK_SECRET=replace_with_a_different_generated_secretUpload both values to their corresponding Worker secrets. Keeping inbound and outbound credentials distinct limits credential reuse across trust boundaries.
Inbound messages that pass Mailbridge processing are delivered to the configured local SMTP server.
Supported destinations include:
- Exchange
- Postfix
- Haraka
- Mailcow
- Any SMTP-compatible local or private mail server
| Variable | Required | Default | Description |
|---|---|---|---|
LOCAL_MAIL_HOST |
Yes |
127.0.0.1 in service code |
Hostname or IP address of the local SMTP destination. |
LOCAL_MAIL_PORT |
No | 25 |
SMTP port of the local delivery server. |
LOCAL_MAIL_SECURE |
No | false |
Enables implicit TLS SMTP, typically used with port 465. |
LOCAL_MAIL_REQUIRE_TLS |
No | true |
Requires STARTTLS when using normal SMTP delivery. |
LOCAL_MAIL_TLS_REJECT_UNAUTHORIZED |
No | true |
Rejects invalid or untrusted TLS certificates. |
LOCAL_MAIL_TLS_SERVERNAME |
No | LOCAL_MAIL_HOST |
TLS server name override, useful when connecting by IP while validating a certificate issued to a hostname. |
LOCAL_MAIL_TLS_CA_FILE |
No | None | Path to an optional PEM CA file used to trust a private SMTP certificate authority. |
LOCAL_MAIL_HOST=mail.internal.example
LOCAL_MAIL_PORT=25
LOCAL_MAIL_SECURE=false
LOCAL_MAIL_REQUIRE_TLS=true
LOCAL_MAIL_TLS_REJECT_UNAUTHORIZED=true
LOCAL_MAIL_TLS_SERVERNAME=mail.internal.example
LOCAL_MAIL_TLS_CA_FILE=LOCAL_MAIL_HOST=10.0.10.15
LOCAL_MAIL_PORT=25
LOCAL_MAIL_SECURE=false
LOCAL_MAIL_REQUIRE_TLS=false
LOCAL_MAIL_TLS_REJECT_UNAUTHORIZED=false
LOCAL_MAIL_TLS_SERVERNAME=
LOCAL_MAIL_TLS_CA_FILE=Warning
Disabling TLS requirements or certificate validation exposes inbound mail to interception or modification between Mailbridge and the local SMTP server. Use this only for controlled testing or protected internal environments.
Mailbridge can relay outbound SMTP submissions through one of four upstream providers:
sendgridresendmailguncloudflare
| Variable | Required | Default | Description |
|---|---|---|---|
RELAY_UPSTREAM_PROVIDER |
When SMTP relay is used | sendgrid |
Selected outbound provider: sendgrid, resend, mailgun, or cloudflare. |
RELAY_API_KEY |
For SendGrid, Resend, or Mailgun | None | Provider API key. Not used for Cloudflare Email Service relay. |
RELAY_FROM_FALLBACK |
Recommended |
postmaster@localhost in service code |
Fallback sender address used when required by outbound processing. |
RESEND_BASE_URL |
Only for custom Resend endpoint | https://api.resend.com |
Resend API base URL. |
MAILGUN_DOMAIN |
For Mailgun | None | Sending domain configured in Mailgun. |
MAILGUN_BASE_URL |
Only for custom Mailgun endpoint | https://api.mailgun.net |
Mailgun API base URL. |
CLOUDFLARE_SEND_WORKER_URL |
For Cloudflare provider | None | Worker endpoint handling Cloudflare Email Service sends, ending in /api/send/email. |
CLOUDFLARE_SEND_WEBHOOK_SECRET |
Recommended for Cloudflare outbound | Falls back to WEBHOOK_SECRET
|
Dedicated secret Mailbridge sends to the Worker for outbound mail authentication. |
RELAY_UPSTREAM_PROVIDER=cloudflare
RELAY_API_KEY=
RELAY_FROM_FALLBACK=postmaster@example.com
CLOUDFLARE_SEND_WORKER_URL=https://mailbridge-worker.example.workers.dev/api/send/email
CLOUDFLARE_SEND_WEBHOOK_SECRET=replace_with_dedicated_outbound_secretRequired Worker binding in wrangler.toml:
[[send_email]]
name = "EMAIL"RELAY_UPSTREAM_PROVIDER=sendgrid
RELAY_API_KEY=replace_with_sendgrid_api_key
RELAY_FROM_FALLBACK=postmaster@example.comRELAY_UPSTREAM_PROVIDER=resend
RELAY_API_KEY=replace_with_resend_api_key
RELAY_FROM_FALLBACK=postmaster@example.com
RESEND_BASE_URL=https://api.resend.comRELAY_UPSTREAM_PROVIDER=mailgun
RELAY_API_KEY=replace_with_mailgun_api_key
RELAY_FROM_FALLBACK=postmaster@example.com
MAILGUN_DOMAIN=mg.example.com
MAILGUN_BASE_URL=https://api.mailgun.netThe SMTP relay accepts outbound mail from trusted local applications and submits it through the selected upstream provider.
The relay is controlled by network source allowlisting and optional TLS. Mailbridge does not expose SMTP username/password authentication configuration.
| Variable | Required | Default | Description |
|---|---|---|---|
SMTP_RELAY_ENABLED |
No | false |
Enables the outbound SMTP relay listener. |
SMTP_RELAY_PORT |
No | 2525 |
Port used by trusted SMTP clients. |
SMTP_RELAY_SOCKET_TIMEOUT_MS |
No | 120000 |
SMTP session socket timeout in milliseconds. |
SMTP_RELAY_MAX_MESSAGE_BYTES |
No | 52428800 |
Maximum SMTP DATA size. Oversized messages receive SMTP 552. |
SMTP_RELAY_VERBOSE_LOGGING |
No | true |
Enables detailed outbound relay logs. |
SMTP_RELAY_INJECT_HEADERS |
No | true |
Adds Mailbridge relay diagnostic headers to submitted outbound messages. |
SMTP_RELAY_REQUIRE_TLS |
No | true |
Requires STARTTLS before accepting a submitted message unless insecure operation is explicitly allowed. |
SMTP_RELAY_ALLOW_INSECURE |
No | false |
Allows mail submission without TLS. Must be true for the unauthenticated non-TLS Quick Start relay. |
SMTP_RELAY_ALLOWED_CIDRS |
Yes when relay is enabled | 127.0.0.1/32,::1/128 |
Comma-separated IPv4 or IPv6 hosts/subnets allowed to connect to the relay. |
SMTP_RELAY_TLS_CERT_FILE |
When TLS is configured | None | Path to the SMTP relay certificate PEM file. |
SMTP_RELAY_TLS_KEY_FILE |
When TLS is configured | None | Path to the SMTP relay private key PEM file. |
SMTP_RELAY_TLS_CA_FILE |
No | None | Optional CA PEM file for SMTP relay TLS. |
This matches the Quick Start deployment:
SMTP_RELAY_ENABLED=true
SMTP_RELAY_PORT=2525
SMTP_RELAY_REQUIRE_TLS=false
SMTP_RELAY_ALLOW_INSECURE=true
SMTP_RELAY_ALLOWED_CIDRS=192.168.1.0/24,127.0.0.1/32,::1/128
SMTP_RELAY_VERBOSE_LOGGING=true
SMTP_RELAY_INJECT_HEADERS=true
SMTP_RELAY_TLS_CERT_FILE=
SMTP_RELAY_TLS_KEY_FILE=
SMTP_RELAY_TLS_CA_FILE=Replace 192.168.1.0/24 with the network that should be permitted to submit outbound mail.
Examples:
# Permit a single application server only
SMTP_RELAY_ALLOWED_CIDRS=10.0.10.25/32,127.0.0.1/32,::1/128
# Permit a trusted internal subnet
SMTP_RELAY_ALLOWED_CIDRS=10.0.20.0/24,127.0.0.1/32,::1/128
# Docker Desktop on macOS may require its bridge network
SMTP_RELAY_ALLOWED_CIDRS=192.168.1.0/24,192.168.65.0/24,127.0.0.1/32,::1/128Never configure:
SMTP_RELAY_ALLOWED_CIDRS=0.0.0.0/0or:
SMTP_RELAY_ALLOWED_CIDRS=::/0Those settings effectively create an unrestricted open relay if port 2525 is reachable.
For trusted clients that support STARTTLS:
SMTP_RELAY_ENABLED=true
SMTP_RELAY_PORT=2525
SMTP_RELAY_REQUIRE_TLS=true
SMTP_RELAY_ALLOW_INSECURE=false
SMTP_RELAY_ALLOWED_CIDRS=10.0.20.0/24,127.0.0.1/32,::1/128
SMTP_RELAY_TLS_CERT_FILE=/app/secrets/smtp-relay-cert.pem
SMTP_RELAY_TLS_KEY_FILE=/app/secrets/smtp-relay-key.pem
SMTP_RELAY_TLS_CA_FILE=When SMTP_RELAY_REQUIRE_TLS=true and SMTP_RELAY_ALLOW_INSECURE=false, Mailbridge requires certificate and private key files to be configured at startup.
Mailbridge supports two SpamAssassin scoring modes:
| Mode | Description |
|---|---|
local |
Starts and uses the in-container spamd daemon. |
postmark |
Uses the Postmark SpamCheck HTTP service and does not start local spamd. |
| Variable | Required | Default | Description |
|---|---|---|---|
SPAMASSASSIN_MODE |
No | local |
Selects local or postmark scoring mode. |
POSTMARK_SPAMCHECK_URL |
For postmark mode |
https://spamcheck.postmarkapp.com/filter |
SpamCheck HTTP endpoint. |
SPAMD_HOST |
For local mode |
127.0.0.1 |
Host where Mailbridge connects to spamd. |
SPAMD_PORT |
For local mode |
783 |
Port where Mailbridge connects to spamd. |
SPAMD_STARTUP_ATTEMPTS |
For container local mode | 30 |
Number of startup checks before failing container startup. |
SPAMC_TIMEOUT_MS |
No | 10000 |
Maximum SpamAssassin request duration in milliseconds. |
SPAMC_FAIL_OPEN |
No | false |
Whether processing may continue when SpamAssassin is unavailable. |
SA_BLOCK_THRESHOLD |
No | 12 |
Spam score threshold for blocking a message. |
SA_QUESTIONABLE_THRESHOLD |
No | 5 |
Score threshold for marking mail as questionable. |
SPAM_SCL_SCORE |
No | 9 |
Spam Confidence Level header value used when spam is identified. |
SPAM_SUBJECT_TAG |
No | [SPAM] |
Text prepended to the subject when a message is marked as spam. |
SPAMASSASSIN_MODE=local
SPAMD_HOST=127.0.0.1
SPAMD_PORT=783
SPAMD_STARTUP_ATTEMPTS=30
SPAMC_TIMEOUT_MS=10000
SPAMC_FAIL_OPEN=false
SA_BLOCK_THRESHOLD=12
SA_QUESTIONABLE_THRESHOLD=5
SPAM_SCL_SCORE=9
SPAM_SUBJECT_TAG=[SPAM]SPAMASSASSIN_MODE=postmark
POSTMARK_SPAMCHECK_URL=https://spamcheck.postmarkapp.com/filter
SPAMC_TIMEOUT_MS=10000
SPAMC_FAIL_OPEN=false
SA_BLOCK_THRESHOLD=12
SA_QUESTIONABLE_THRESHOLD=5
SPAM_SCL_SCORE=9
SPAM_SUBJECT_TAG=[SPAM]Spamhaus sender reputation checks are optional and disabled by default.
| Variable | Required | Default | Description |
|---|---|---|---|
SPAMHAUS_ENABLED |
No | false |
Enables Spamhaus Intelligence API checks. |
SPAMHAUS_USERNAME |
When enabled | None | Spamhaus API username. |
SPAMHAUS_PASSWORD |
When enabled | None | Spamhaus API password. |
SPAMHAUS_FAIL_OPEN |
No | true |
Allows mail processing to continue when Spamhaus checks fail. |
Example:
SPAMHAUS_ENABLED=true
SPAMHAUS_USERNAME=replace_with_username
SPAMHAUS_PASSWORD=replace_with_password
SPAMHAUS_FAIL_OPEN=trueMailbridge uses the original sender IP contained in the inbound Worker payload rather than treating the Cloudflare webhook request IP as the original sender.
Mailbridge can optionally perform a secondary AI-based spam screening pass. It is disabled by default.
| Variable | Required | Default | Description |
|---|---|---|---|
AI_ENABLED |
No | false |
Enables AI secondary screening. |
AI_API_KEY |
When enabled | None | API key used by the configured AI endpoint. |
AI_MODEL |
When enabled |
gpt-5.4-nano in example config |
Model identifier used for classification. |
AI_BASE_URL |
Depends on provider | None | Optional alternate API base URL. |
AI_INPUT_SCOPE |
No | headers |
Controls content exposed to AI review. Supported values are headers, attachments, and full_email. |
AI_MAX_INPUT_CHARS |
No | 20000 |
Maximum input size sent to the AI classifier. |
AI_ENABLED=true
AI_API_KEY=replace_with_api_key
AI_MODEL=gpt-5.4-nano
AI_BASE_URL=
AI_INPUT_SCOPE=headers
AI_MAX_INPUT_CHARS=20000| Scope | Information submitted for AI review |
|---|---|
headers |
Email headers only. |
attachments |
Headers plus attachment filenames; attachment contents are not submitted. |
full_email |
Full email content, subject to configured maximum length. |
Caution
Before enabling AI screening, review privacy, confidentiality, and compliance requirements for the email content that may be transmitted to an AI provider.
Mailbridge uses a Cloudflare Worker for inbound Email Routing processing, encrypted R2 storage, Queue delivery, and optional Cloudflare Email Service outbound sends.
Configure these in wrangler.toml:
| Variable | Required | Description |
|---|---|---|
NODE_APP_URL |
Yes | Public Mailbridge webhook URL. When configured as an origin without a path, the Worker appends /api/webhook/email. |
MAIL_STORE_ENCRYPTION_VERSION |
Recommended | Encryption payload version stored with R2 messages. Current encrypted flow uses v1. |
Example:
[vars]
NODE_APP_URL = "https://mailbridge.example.com/api/webhook/email"
MAIL_STORE_ENCRYPTION_VERSION = "v1"| Secret | Required | Description |
|---|---|---|
WEBHOOK_SECRET |
Yes | Authenticates inbound Worker webhook submissions to Mailbridge. |
CLOUDFLARE_SEND_WEBHOOK_SECRET |
For Cloudflare outbound | Authenticates Mailbridge requests to the Worker's /api/send/email endpoint. |
MAILBRIDGE_PUBLIC_KEY_PEM |
For encrypted inbound storage | Public key used to encrypt inbound message payloads before storing them in R2. |
Upload secrets:
printf '%s' "$WEBHOOK_SECRET" | \
npx wrangler secret put WEBHOOK_SECRET --name "$WORKER_NAME"
cat secrets/mailbridge-r2-public.pem | \
npx wrangler secret put MAILBRIDGE_PUBLIC_KEY_PEM --name "$WORKER_NAME"
printf '%s' "$CLOUDFLARE_SEND_WEBHOOK_SECRET" | \
npx wrangler secret put CLOUDFLARE_SEND_WEBHOOK_SECRET --name "$WORKER_NAME"| Binding | Resource | Required For |
|---|---|---|
MAIL_STORE |
Private R2 bucket | Encrypted inbound mail storage. |
MAIL_QUEUE |
Cloudflare Queue | Inbound mail delivery from Worker to Mailbridge. |
EMAIL |
Cloudflare Email Service binding | Outbound relay when RELAY_UPSTREAM_PROVIDER=cloudflare. |
Example wrangler.toml:
name = "mailbridge-worker"
main = "worker.js"
compatibility_date = "2026-05-19"
preview_urls = false
[vars]
NODE_APP_URL = "https://mailbridge.example.com/api/webhook/email"
MAIL_STORE_ENCRYPTION_VERSION = "v1"
[[r2_buckets]]
binding = "MAIL_STORE"
bucket_name = "mailbridge-inbound"
[[queues.producers]]
binding = "MAIL_QUEUE"
queue = "mailbridge-inbound"
[[queues.consumers]]
queue = "mailbridge-inbound"
max_batch_size = 10
max_batch_timeout = 5
max_retries = 3
[[send_email]]
name = "EMAIL"The [[send_email]] block must be top-level. It must not be nested inside [[queues.consumers]].
The repository Docker Compose configuration reads .env, mounts local data and secrets directories, and publishes both HTTP and SMTP relay ports.
services:
mail-bridge:
build: .
container_name: mail-bridge
restart: unless-stopped
ports:
- "3090:3090"
- "2525:2525"
env_file: .env
volumes:
- "./data:/app/data"
- "./secrets:/app/secrets"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3090/health"]
interval: 30s
timeout: 10s
retries: 3Start Mailbridge:
docker compose up -d --buildCheck health:
curl -i http://127.0.0.1:3090/healthExpected response:
OK
Because port 2525 accepts mail without SMTP authentication in the Quick Start configuration, prefer firewall restrictions in addition to the Mailbridge CIDR allowlist.
For example, if only 10.0.20.25 should submit mail, configure:
SMTP_RELAY_ALLOWED_CIDRS=10.0.20.25/32,127.0.0.1/32,::1/128Then restrict the host firewall so only that system can reach TCP port 2525.
Before starting Mailbridge, verify:
-
WEBHOOK_SECRETis configured in.env. - The Cloudflare Worker
WEBHOOK_SECRETmatches the Mailbridge value. -
QUEUE_MASTER_KEYhas been generated and stored securely. -
MAILBRIDGE_PRIVATE_KEY_PATHpoints to the local private key. - The matching public key has been uploaded as
MAILBRIDGE_PUBLIC_KEY_PEM. -
LOCAL_MAIL_HOSTandLOCAL_MAIL_PORTpoint to the inbound destination SMTP server. -
RELAY_UPSTREAM_PROVIDERis set to the intended outbound provider. - When using Cloudflare outbound relay,
CLOUDFLARE_SEND_WORKER_URLpoints to/api/send/email. - When using Cloudflare outbound relay, a distinct
CLOUDFLARE_SEND_WEBHOOK_SECRETis configured in Mailbridge and the Worker. - When
SMTP_RELAY_ENABLED=true,SMTP_RELAY_ALLOWED_CIDRScontains only trusted clients or networks. - Neither
0.0.0.0/0nor::/0appears in the relay allowlist. - SMTP relay port
2525is not publicly reachable. -
.env,data/,secrets/, private keys, tokens, and database files are excluded from source control.
Copyright (c) 2026 Voxvey Research LLC, a Helio company.