Your app needs to send transactional email: sign-up confirmations, password resets, order receipts, alerts. These must arrive quickly and land in the inbox. You can send them from your own mail server on a VPS, or through a transactional email service that handles reputation, scaling and deliverability for you. Here’s how to choose, and how to set one up with your app.
Own mail server vs a service
| Own Postfix server on a VPS | Transactional email service | |
|---|---|---|
| Cost | Server only | Free tier, then per 1,000 emails |
| Deliverability work | You manage PTR, SPF, DKIM, DMARC, IP reputation and blocklists | Mostly handled. You still set up SPF, DKIM and DMARC for your domain |
| Scaling and bursts | Limited by your IP’s reputation and warm-up | Built for volume |
| Analytics | Logs only | Opens, clicks, bounces, complaints, webhooks |
| Control and privacy | Full | Data passes through the provider |
Many setups use both: a self-hosted mail server for mailboxes, and a service for application email. Or simply a service for everything the app sends. See email deliverability.
Popular services
| Service | Strengths | Notes |
|---|---|---|
| Amazon SES | Very low cost at volume, scales enormously | Starts in a sandbox (you request production access). Fewer built-in UI features |
| Postmark | Excellent deliverability focus for transactional mail, fast | Separates transactional and broadcast streams. Higher price per email |
| SendGrid (Twilio) | Popular, transactional + marketing, good APIs | Shared IPs on lower plans |
| Mailgun | Developer-friendly API, routing and inbound parsing | Free tier limited |
| Brevo (ex-Sendinblue) | Transactional + marketing, generous free tier | Marketing-oriented UI |
| Resend | Modern developer experience, React email templates | Newer service |
| Mailjet, SparkPost and others | Various regional and pricing advantages | Compare data residency |
Choose based on volume, price, region and data location, and features like webhooks, templates and inbound processing. Check current pricing and free tiers. They change often.
Setup steps (any provider)
1. Verify your sending domain
Add the DNS records the provider gives you:
- DKIM (TXT or CNAME records), required for alignment;
- SPF: add the provider’s
include:to your existing SPF record (one SPF record per domain!); - a custom return-path / bounce domain (a CNAME), so SPF aligns with your domain;
- DMARC, if you don’t have it yet.
example.com. TXT "v=spf1 ip4:203.0.113.25 include:amazonses.com -all"
See SPF, DKIM and DMARC and TXT records.
Consider sending from a subdomain (mail.example.com or notify.example.com) to separate its reputation from your main domain.
2. Connect your app: SMTP or API
SMTP (works with almost any app or framework):
host: email-smtp.us-east-1.amazonaws.com (provider-specific)
port: 587 (STARTTLS) or 465 (TLS)
user / password: SMTP credentials from the provider
See SMTP ports 25, 465 and 587. For WordPress, use an SMTP plugin (WP Mail SMTP, FluentSMTP) with these settings.
HTTP API (more features, often faster and more reliable from apps):
curl -s https://api.postmarkapp.com/email \
-H "X-Postmark-Server-Token: $TOKEN" -H "Content-Type: application/json" \
-d '{"From":"[email protected]","To":"[email protected]","Subject":"Reset your password","TextBody":"…"}'
Official SDKs exist for Node, Python, PHP, Ruby and Go.
Store credentials as secrets, not in code. See secrets management.
3. Handle bounces and complaints
Configure webhooks (or the SES SNS notifications) so your app learns about hard bounces and spam complaints, and stops emailing those addresses. Repeatedly sending to bad addresses damages reputation, and providers may suspend your account.
4. Send from a queue
Send email from a background job queue (Laravel queues, Celery, Sidekiq, BullMQ) instead of inside the web request. Users don’t wait, and temporary failures retry automatically. See Laravel queues with Supervisor.
Good practices
- Separate transactional and marketing streams (different subdomains, IPs or provider streams).
- Plain-text plus HTML versions, a clear From name, and no misleading subjects.
- One-click unsubscribe headers for anything non-essential (required for bulk senders by Gmail and Yahoo).
- Monitor delivery dashboards and Google Postmaster Tools.
Quick reference
| Step | What |
|---|---|
| Domain | DKIM + SPF include + return-path CNAME + DMARC |
| App connection | SMTP 587/465, or HTTP API |
| Feedback | Bounce and complaint webhooks |
| Reliability | Send from a job queue |
| Reputation | Separate transactional and marketing |
Related: MX records and IP blacklists.