Sending Email From Your App — Use an API, and Own the Domain
Every app eventually needs to send something — a password reset, a receipt, an alert. There's one constraint here that settles the design before you write any code: your app's generated hostname is a subdomain of the platform, not a domain you control, so you can't publish the DNS records that make mail from it trustworthy. And you need those records — Google's sender guidelines require senders to "set up SPF or DKIM email authentication for your sending domains" (Google). The practical answer is short: send through an email provider's HTTPS API, from a domain you own, and keep SMTP out of your container entirely.
Why the domain question comes first
Email authentication is DNS-based. SPF, DKIM, and DMARC are all records published under the domain in your From: address, and receiving servers check them against it.
Your app is reachable at a generated hostname under the platform's own domain. You can't add records to it, which means you can't authenticate mail claiming to come from it. Sending as noreply@<your-app-hostname> isn't a shortcut — it's mail that fails the checks receiving servers are increasingly strict about.
So the sending identity has to be a domain you own, whether that's the one your users know or something like mail.yourproject.com. That's true on any platform; it's just unusually visible here because there's no domain of your own attached to the app.
Use the provider's API, not SMTP
Once a provider is in the picture, you get a choice of transports, and the HTTPS API is the better fit for a small container in every respect that matters:
- One outbound HTTPS request rather than a multi-step SMTP conversation held open while you wait.
- Immediate, structured errors. A 4xx with a JSON body beats parsing an SMTP status code, and it's far easier to log usefully.
- No connection state to manage. SMTP connections are long-lived and stateful — exactly the thing that doesn't survive a container being replaced on every deploy.
- Fewer surprises about ports. Outbound SMTP ports are commonly restricted across the internet generally; HTTPS is the path that always works.
If your framework only speaks SMTP, most providers offer an SMTP endpoint too, and it will work. The API is simply less to go wrong.
The records you'll be asked to publish
Your provider will give you exact values. What they're for:
- SPF — lists who may send for your domain. Publishing your provider's include is usually a one-line record.
- DKIM — a signature proving the message wasn't altered and came from an authorised sender. The provider gives you a key to publish.
- DMARC — tells receivers what to do when the first two fail, and where to send reports. Google's guidance for bulk senders is to "set up DMARC email authentication for your sending domain", noting "your DMARC enforcement policy can be set to none" — so you can start in reporting mode and tighten later.
Two numbers worth knowing from the same guidance: keep spam complaints "below 0.3%", and for marketing or subscribed mail, support "one-click unsubscribe" with a visible link. Transactional mail from a small app rarely approaches those thresholds, but "rarely" is doing work — a password reset flow that someone abuses to mail strangers will.
Don't send from the request
The pattern that bites on a small container: calling the email API inline while a user waits.
A provider hiccup then becomes your latency, and on a fractional CPU where your allocation is a time budget, a handler blocked on a slow external call is holding a slice of everything.
Better shape: record the intent, return, and send from a background loop. That also gives you a retry that survives a restart — in-memory queues don't, and a container is recreated on every deploy.
For a small app, "record the intent" can be a row in SQLite on a volume with a sent_at column. That's a complete outbox.
Failures you should handle deliberately
- Hard bounces — the address doesn't exist. Stop sending to it; repeated attempts hurt your reputation.
- Complaints — someone marked it spam. Suppress immediately.
- Rate limits — providers have them. Back off with jitter rather than retrying in a tight loop.
- Provider outages — your outbox should hold, not drop.
Most providers can call a webhook for bounces and complaints, which is an ordinary HTTP endpoint you can host — and one worth verifying the signature on, since it's public.
Sending yourself alerts is a different problem
If the mail is for you — "the backup finished", "an error occurred" — email is a heavier tool than you need. It carries all of the above: a domain, records, reputation.
For notifications to yourself, a self-hosted ntfy instance or a Discord webhook is a single HTTP request with no deliverability story at all. Keep email for messages to other people, where the recipient expects mail specifically.
FAQ
Can I send email from my app's default hostname?
Not credibly. It's a subdomain of the platform, so you can't publish SPF or DKIM records for it, and unauthenticated mail is increasingly rejected.
Should I use SMTP or an HTTPS API?
The API. One request, structured errors, no long-lived connection to lose when your container is replaced.
What records do I need?
SPF and/or DKIM at minimum — Google's guidance requires authentication for all senders — plus DMARC, which can start with an enforcement policy of none.
Why is my mail going to spam?
Most often missing or mismatched authentication for the From: domain. Check that SPF and DKIM both pass for the exact domain you're sending as.
Should I send email during the request?
No. Record it and send from a background loop, so a slow provider isn't your latency and a restart doesn't lose the message.
What about alerts just for me?
Use a push service or a chat webhook. Email brings deliverability requirements you don't need for messages to yourself.
Wrapping up
Email is the one integration where the hosting question is really a DNS question. You can't authenticate mail from a hostname you don't control, so the first decision is which domain you're sending as — and everything after that is ordinary: a provider's API over HTTPS, an outbox row so a redeploy doesn't lose a message, and suppression lists so failures don't compound. If the mail is only ever going to you, skip all of it and send yourself a push notification instead.