API v1Private beta. The API is stable; send limits are low while the shared domain warms up.

Self-hosting

Configuration reference

Every setting for the Docker and Cloudflare editions, with defaults and rules.

Both editions read their settings once at start and check all of them together. A bad setting stops send0 with a list of every problem, naming the variable but never its value. Values are trimmed, and blank means unset.

Docker Compose

Set these in .env, next to compose.yml. Compose passes the whole file to the send0 container. After a change, run docker compose up -d to recreate the container; docker compose restart doesn't reload .env. Run docker compose exec send0 send0 doctor to check them.

Required

NameDefaultRequiredDescription
DOMAINnoneYesThe hostname people and SDKs use, like mail.example.com. A hostname only: no scheme, port or path. Caddy gets an HTTPS certificate for it.
MAIL_DOMAINDOMAINNoThe domain your inboxes live on, like agents.example.com. Point its MX at DOMAIN. Set this or MAIL_DOMAINS, not both.
OWNER_EMAILnoneYes, unless ALLOW_SIGNUP=trueThe only email that can create the first account, which becomes the owner. Must be an email address.
SECRET_KEYnoneYesSigns download links for files on local disk. At least 32 characters: openssl rand -hex 32.
POSTGRES_PASSWORDnoneYes (compose)Used by the postgres service and inside DATABASE_URL, so keep it URL-safe: openssl rand -hex 24. Read by compose, not by send0.
SMTP_URLnoneYes, when MAILER=smtpThe outbound relay: smtp://user:pass@host:587 (STARTTLS) or smtps://user:pass@host:465. Must include a port. Checked at start. See SMTP_URL.

Mail

NameDefaultRequiredDescription
MAIL_DOMAINSMAIL_DOMAINNoSeveral mail domains, comma-separated. The first is the default for new inboxes. Each must be a domain name with a TLD, like agents.example.com.
MAIL_FROMnoreply@<first mail domain>NoSender of system email: verification, password resets and invites. Must be an email address.
OPERATOR_FORWARD_TOnoneNoWhere mail for postmaster@ and abuse@ on your mail domains goes. send0 relays it through your mailer, from MAIL_FROM, with the original attached. Unset, that mail is refused. Must be an email address, and not a reserved one on your own mail domains.
MX_HOSTNAMEDOMAINNoThe name this MX uses in its greeting, its self-signed certificate and the Authentication-Results it writes. Must be a hostname.
SMTP_TLS_CERTnoneSet both or neitherPath inside the container to a certificate (PEM) for inbound STARTTLS. Without it, send0 makes a self-signed certificate for MX_HOSTNAME.
SMTP_TLS_KEYnoneSet both or neitherPath inside the container to that certificate's private key.
SMTP_MAX_CLIENTS30NoConcurrent inbound SMTP sessions, from 1 to 10000. Each can buffer a message of about 25 MiB, so this bounds memory.
TRUSTED_AUTHSERV_IDSnoneNoComma-separated authserv-ids whose Authentication-Results headers are trusted. MX_HOSTNAME is always trusted.
MAILERsmtpNosmtp (uses SMTP_URL) or ses.
SES_REGIONnoneYes, when MAILER=sesThe region of your SES identity, like us-east-1.
SES_ACCESS_KEY_IDnoneYes, when MAILER=sesAccess key of an IAM user allowed ses:SendRawEmail. Add ses:GetAccount so send0 doctor can check the account.
SES_SECRET_ACCESS_KEYnoneYes, when MAILER=sesThat user's secret key.
SES_CONFIGURATION_SETnoneYes, when MAILER=sesThe SES configuration set to send with.
SES_EVENTS_TOKENnoneSet both or neitherOnly with MAILER=ses. At least 32 characters. The secret in the SNS subscription URL https://DOMAIN/internal/ses-events?token=….
SES_EVENTS_TOPIC_ARNnoneSet both or neitherOnly with MAILER=ses. The SNS topic that carries SES events. Messages from any other topic are refused.

Storage

NameDefaultRequiredDescription
BLOB_DRIVERfsNoWhere raw mail and attachments go: fs (local disk) or s3.
BLOB_DIR/data/blobsNoThe directory for BLOB_DRIVER=fs. The image sets it to /data/blobs, on the blobs volume.
S3_BUCKETnoneYes, when BLOB_DRIVER=s3Bucket name.
S3_REGIONnoneYes, when BLOB_DRIVER=s3Bucket region.
S3_ACCESS_KEY_IDnoneYes, when BLOB_DRIVER=s3Access key with read and write access to the bucket.
S3_SECRET_ACCESS_KEYnoneYes, when BLOB_DRIVER=s3Its secret key.
S3_ENDPOINTnoneNoFor S3-compatible stores other than AWS (R2, MinIO, Backblaze), like https://<account>.r2.cloudflarestorage.com. Must be an http:// or https:// URL. Addressed path-style.

Advanced

NameDefaultRequiredDescription
SEND0_VERSIONlatestNoThe image tag compose runs, like 0.2.0 or 0.2. Read by compose, not by send0.
PUBLIC_URLhttps://DOMAINNoWhere people reach send0, if not https://DOMAIN. An origin only (scheme, host and optional port), no path. Used for links in email, download links and the dashboard's CSRF check.
ALLOW_SIGNUPfalseNotrue lets anyone sign up once the owner exists. false: others join by invite. Accepts true, false, 1 or 0.
LIMITSnoneNohosted turns on the hosted service's limits: reply-only free plan, daily send caps and inbox caps. none turns them off.
HOST0.0.0.0NoHTTP bind address inside the container. An IP address or hostname.
PORT3000NoHTTP port inside the container, 0 to 65535. If you change it, change send0:3000 in the Caddyfile.
TRUSTED_PROXIES127.0.0.0/8,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fc00::/7NoProxies whose X-Forwarded-For is believed, as IPs or CIDR ranges, comma-separated, or none. The default covers Caddy on the compose network.

Set by compose and the image

You don't normally set these. They're listed because send0 reads them.

NameDefaultRequiredDescription
DATABASE_URLSet by compose.yml from POSTGRES_PASSWORDYesA postgres:// or postgresql:// URL. Compose's value overrides one in .env; edit compose.yml to use another Postgres.
SMTP_PORT2525NoInbound SMTP port inside the container. Compose maps host port 25 to it.
WEB_DIR/app/web in the imageNoThe built dashboard.
MIGRATIONS_DIR/app/migrations in the imageNoThe SQL migrations.

Cloudflare

Plain settings are vars in wrangler.jsonc. Secrets are set with the Deploy button's prompts, wrangler secret put, or under Workers & Pages > send0 > Settings > Variables and Secrets. Either kind works for any setting; secrets keep the value hidden.

NameDefaultRequiredDescription
MAIL_DOMAINSagents.example.com (var)YesDomains you receive mail on, comma-separated. The first is the default for new inboxes. Route each one's Email Routing catch-all to the Worker. The example.com placeholder is refused.
SES_REGIONus-east-1 (var)YesThe AWS region of your verified SES identity. Must look like an AWS region.
ALLOW_SIGNUPfalse (var)Notrue lets anyone sign up once the owner exists. Accepts true, false, 1 or 0.
SECRET_KEYnone (secret)YesSigns download links. At least 32 characters: openssl rand -hex 32.
OWNER_EMAILnone (secret)Yes, unless ALLOW_SIGNUP=trueThe only email that can create the first account, which becomes the owner.
SES_ACCESS_KEY_IDnone (secret)YesAccess key of an IAM user allowed ses:SendRawEmail for your SES identity.
SES_SECRET_ACCESS_KEYnone (secret)YesThat user's secret key.
MAIL_FROMnoreply@<first mail domain>NoSender of verification, password-reset and invite email. Must be an email address.
PUBLIC_URLEach request's own originNoThe one URL people and SDKs use, like https://mail.example.com. An origin only. Requests to any other hostname, workers.dev included, are redirected to it.
OPERATOR_FORWARD_TOnoneNoWhere mail for postmaster@ and abuse@ on your mail domains goes, forwarded by Email Routing. Must be a verified destination address (Email Routing > Destination addresses). Unset, that mail is refused.
SES_CONFIGURATION_SETnoneNoThe SES configuration set to send with. Unset, SES uses the account's default configuration set, if any.
SES_EVENTS_TOKENnoneSet both or neitherAt least 32 characters. The secret in the SNS subscription URL https://<your URL>/internal/ses-events?token=….
SES_EVENTS_TOPIC_ARNnoneSet both or neitherThe SNS topic that carries SES events. Messages from any other topic are refused.
TRUSTED_AUTHSERV_IDSmx.cloudflare.netNoComma-separated authserv-ids whose Authentication-Results headers are trusted. mx.cloudflare.net is always trusted; these add to it.
LIMITSnoneNohosted turns on the hosted service's limits; none turns them off.

Bindings

wrangler.jsonc declares these. The Deploy button creates the resources.

BindingTypeDefault name
HYPERDRIVEHyperdriveYour Postgres
BLOBSR2 bucketsend0-blobs
EVENTSQueue (producer and consumer)send0-events
HUBDurable Objectclass Hub
ASSETSStatic assetsThe dashboard
RL_KEY RL_KEY_SEND RL_IPRate LimitingAPI rate limits

On this page