The Quick start CLI does all of this for you. Prefer to wire it up by hand? It’s three steps.
Install Postboi
Set your provider and credentials
Send
What lives where
Postboi splits config along a single line: secrets in the environment, everything else in
the committed config file. Both are read by mail() on every call.
Everything in the config file can still be overridden by an env var — handy for
per-environment values without editing committed config (see below). The override names are POSTBOI_PROVIDER, POSTBOI_FROM / POSTBOI_TO / …, and each provider’s own field vars
(e.g. MAILGUN_DOMAIN). Env always wins.
Per-environment config
Two portable patterns, in order of preference.
1. Override the provider with an env var. Keep a safe default in the committed file and flip it on the host. This works on every runtime — Node, Bun, and edge — with no magic:
Local dev (nothing set) uses mock; production (env set) uses Resend. POSTBOI_PROVIDER always wins over the file.
2. Use SvelteKit’s dev flag in a hook. The config file is imported directly by
Node, so it can’t read $app/environment. Configure from SvelteKit’s init hook instead, where the flag
is available — it runs once at startup and works on every adapter, including Workers:
Avoid
process.env.NODE_ENVin the config file — a self-hosted Node deploy that forgets to set it falls into the wrong branch silently. Prefer the two patterns above.
On SvelteKit
A contact-form action is a one-liner. See SvelteKit form actions.
On runtimes without ambient env vars (e.g. Cloudflare Workers), construct the provider directly — see Using a provider directly.