SMTP Configuration Tutorial: Building Your Email Sending System from Scratch
SMTP (Simple Mail Transfer Protocol) is the standard protocol for sending email on the internet. This article covers two approaches: using third-party services (recommended) and self-hosting.
1. SMTP Basics
1.1 SMTP Ports
| Port | Encryption | Notes |
|---|---|---|
| 25 | None/STARTTLS | Traditional, often blocked |
| 465 | SSL/TLS | SMTPS, recommended |
| 587 | STARTTLS | Submission port, recommended |
Use port 587 (STARTTLS) or 465 (SSL). Port 25 is often blocked by ISPs.
1.2 A Real SMTP Session
You can send a test email by hand with swaks or telnet, which is more intuitive than any tutorial. A typical session looks like this:
swaks --to [email protected] --from [email protected] \
--server smtp.sendgrid.net:587 --tls \
--header "Subject: Hello" --body "SMTP works!"
The conversation between server and client has four steps: EHLO (greeting and capability negotiation, such as STARTTLS), MAIL FROM (declaring the sender), RCPT TO (declaring the recipient), and DATA (the body, terminated by a lone .). A 250 at each step means success; a 550 usually means the recipient does not exist or an anti-spam policy rejected it. Understanding this dialog lets you tell whether "mail won't send" is a connection, authentication, or rejection problem instead of guessing.
2. Using Third-Party Email Services (Recommended)
2.1 Provider SMTP Configs
| Provider | Server | Port | Auth |
|---|---|---|---|
| SendGrid | smtp.sendgrid.net | 587/465 | API Key |
| Mailgun | smtp.mailgun.org | 587/465 | Login+Password |
| Resend | smtp.resend.com | 587/465 | API Key |
| Amazon SES | email-smtp.region.amazonaws.com | 587/465 | SMTP credentials |
2.2 Deliverability Trio: SPF / DKIM / DMARC
Once the provider is configured, landing in the inbox still depends on three DNS records on your domain. SPF declares "who is allowed to send as your domain", DKIM cryptographically signs mail so recipients can verify it truly came from your server, and DMARC tells receivers what to do when verification fails. For SendGrid, a typical setup is:
yourdomain.com. TXT "v=spf1 include:sendgrid.net ~all"
sg._domainkey.yourdomain.com. TXT "k=rsa; p=MIGfMA0... (public key from SendGrid)"
_dmarc.yourdomain.com. TXT "v=DMARC1; p=none; rua=mailto:[email protected]"
Start with p=none and watch the reports for a while; once no legitimate mail fails, tighten gradually to p=quarantine or p=reject. Beginners often skip this step and then wonder why everything lands in spam despite successful sends. Full details are in the SPF/DKIM/DMARC setup.
2.3 Node.js + Nodemailer
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
host: 'smtp.sendgrid.net',
port: 587,
auth: {
user: 'apikey',
pass: process.env.SENDGRID_API_KEY,
},
});
await transporter.sendMail({
from: '"App" <[email protected]>',
to: '[email protected]',
subject: 'Password Reset',
html: `<p>Click to reset: <a href="${resetLink}">Reset</a></p>`,
});
2.3 Laravel (.env)
MAIL_MAILER=smtp
MAIL_HOST=smtp.sendgrid.net
MAIL_PORT=587
MAIL_USERNAME=apikey
MAIL_PASSWORD=SG.xxxxx
MAIL_ENCRYPTION=tls
[email protected]
3. Self-Hosting (Advanced)
3.1 Postfix + Dovecot
apt update && apt install postfix dovecot-core dovecot-imapd
postconf -e 'myhostname = mail.yourdomain.com'
systemctl restart postfix
3.2 Self-Hosted vs Third-Party
| Dimension | Self-Hosted | Third-Party |
|---|---|---|
| Cost | Server only | Per-volume or monthly |
| Deliverability | Lower (need IP warm-up) | High (shared IP reputation) |
| Control | Full | Limited |
| Maintenance | High | Low |
3.3 If You Self-Host Anyway: IP Warm-Up and Reverse DNS
The biggest hurdle of a self-hosted server is not installing software but "getting the internet to trust you". A fresh IP starts with zero sending reputation; blasting large volumes immediately mostly lands in spam. You need to warm up the IP with a "small, gradually increasing" daily volume, usually for several weeks. At the same time, three things must be in place: a correct reverse DNS record (a PTR pointing at mail.yourdomain.com), SPF/DKIM/DMARC fully configured, and outbound port 25 not blocked by your host (many cloud providers block port 25 by default and require a support ticket to unblock). Without these three, no matter how polished your Postfix setup, the mail you send is just sinking into the void.
4. Best Practices
Transactional vs. Marketing Email
The two kinds of email demand completely different sending strategies. Transactional mail (registration verification, password resets, order notifications) is "expected" — recipients actively look for it, so deliverability matters more and volume stays modest. Marketing mail (promotions, digests, re-engagement) is high-volume and can annoy recipients, so frequency and unsubscribe mechanics are critical. In practice, send transactional mail through a high-reputation channel (possibly a dedicated domain) and marketing mail from a separate subdomain (e.g. marketing.yourdomain.com) with strict rate limiting, so marketing never drags down your domain reputation and pulls transactional mail into spam with it.
4.1 Error Handling with Retry
async function sendWithRetry(emailData, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await transporter.sendMail(emailData);
} catch (error) {
if (attempt === maxRetries) throw error;
await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1000));
}
}
}
4.2 Queue Processing
Use message queues (Bull, RabbitMQ) for high-volume sending.
5. Common Issues
| Issue | Cause | Fix |
|---|---|---|
| Timeout | Port blocked | Try 587 or 465 |
| Auth failed | Wrong API key | Check permissions |
| Rejected (550) | Low IP reputation | Configure SPF/DKIM |
| Spam folder | Content issues | Optimize content |
6. Summary
For most applications, use third-party SMTP (SendGrid/Mailgun/Resend) with proper SPF/DKIM/DMARC. Self-host only for special needs (data compliance, millions of emails/month).
References: https://datatracker.ietf.org/doc/html/rfc5321, https://www.spf-dkim-dmarc.org/, https://docs.sendgrid.com/for-developers/sending-email/sender-identity