Tanstack Start
Self-hosting

Email

Feeblo sends transactional email over SMTP using nodemailer. Configure the SMTP_* variables; dev defaults point at Mailpit.

Transactional email (verification codes, etc.) is sent through a single nodemailer SMTP transport built from the SMTP_* environment variables. There is no separate mail driver selection — one transport, configured by env.

This page is based on packages/transactional/src/mailer.ts, packages/transactional/src/config.ts, and .env.example in .reference/.

How it sends

packages/transactional/src/mailer.ts builds a nodemailer transport with these knobs:

  • Renders an email's React template to HTML via @react-email/render, derives a plain-text version with toPlainText, and sends both.
  • Sets from to the configured SMTP_FROM_ADDRESS unless the caller overrides it per-message (with an optional replyTo).

Environment variables

These are the variables MailerConfig (packages/transactional/src/config.ts) actually reads at runtime, with the in-code defaults:

Prop

Type

SMTP_SECURE and SMTP_UNSAFE_IGNORE_TLS are parsed as the literal strings "true" / "false"; anything else fails to start with a config error (Expected … to be "true" or "false"). SMTP_PORT must parse as an integer.

What .env.example declares vs what the code reads

.env.example ships two more variables the comments describe but the code does not consume in this snapshot:

  • SMTP_FROM_NAME="Feeblo" — declared, but MailerConfig only builds a plain address for from. The "Feeblo" name is not currently attached to the From header.
  • SMTP_TRANSPORT (commented options: smtp-auth / smtp-api / resend) — declared in the example as a transport selector, but MailerConfig builds a single nodemailer SMTP transport and never reads SMTP_TRANSPORT. Resend / SMTP-API variants are not wired up here.

Don't rely on the commented transport options

If you set SMTP_TRANSPORT=resend expecting the Resend API to be used, nothing changes — Feeblo will still build an SMTP transport from SMTP_HOST/SMTP_PORT/SMTP_*. Configure your Resend account over SMTP (Resend exposes an SMTP endpoint), or wait for the documented selection to land in code. Setting SMTP_FROM_NAME likewise has no effect on the From header today.

The fields the code does read all have sensible defaults, so for purely local development you can leave most of them unset and point at the dev Mailpit container.

Local development with Mailpit

The dev compose runs Mailpit (axllent/mailpit). Mailpit accepts SMTP on 1025 and shows a web UI on http://localhost:8025 where you can read every message Feeblo sends. The example values match:

SMTP_HOST="127.0.0.1"
SMTP_PORT=1025
SMTP_FROM_ADDRESS="noreply@feeblo.com"
# No auth, no TLS — leave SMTP_USERNAME / SMTP_PASSWORD / SMTP_SECURE unset

The dev port differs from the code default

MailerConfig defaults SMTP_PORT to 2500 when SMTP_PORT is unset, but the example sets it to 1025 to match Mailpit. If you remove that line you'll send to port 2500 on your SMTP host, which is almost certainly not what you want.

Production providers

For a hosted SMTP provider, set the auth credentials and turn TLS on:

SMTP_HOST="smtp.your-provider.com"
SMTP_PORT=465
SMTP_SECURE="true"
SMTP_USERNAME="apikey"
SMTP_PASSWORD="your-smtp-password"
SMTP_FROM_ADDRESS="noreply@feeblo.example.com"

SMTP_SECURE="true" is right for port 465 (implicit TLS). For STARTTLS-on-587, leave SMTP_SECURE false and don't set SMTP_UNSAFE_IGNORE_TLS — nodemailer will negotiate STARTTLS when the server advertises it. Set SMTP_UNSAFE_IGNORE_TLS="true" only in deliberately insecure tracer setups.

Alternatively, set SMTP_SERVICE to a nodemailer service name (e.g. "gmail", "outlook") and you can omit SMTP_HOST/SMTP_PORT:

SMTP_SERVICE="gmail"
SMTP_USERNAME="you@gmail.com"
SMTP_PASSWORD="app-password"
SMTP_FROM_ADDRESS="you@gmail.com"

Templates

Email bodies are React Email templates under packages/transactional/src/templates/. They're rendered server-side with @react-email/render; the package also ships an email dev script (pnpm --filter @feeblo/transactional email dev) that runs the React Email preview server on port 3005 for iterating on templates without sending real mail.

On this page