SMTP
The SMTP driver — when to use it, what ports work, and the Gmail app-password walkthrough.
Cloud providers block SMTP ports
- GCP — Port 25 blocked permanently, cannot request unblock.
- AWS EC2 — Port 25 blocked by default, unblock requests often denied.
- DigitalOcean — Port 25 blocked for all new accounts.
- Azure — Port 25 blocked.
Port 587 (STARTTLS) usually works, but some providers restrict it too.
If you're deploying on cloud, use Mailgun, SendGrid, or SES instead of SMTP.
When to use SMTP
- On-premise / bare-metal servers.
- Corporate mail servers (Exchange, Postfix).
- Self-hosted mail (Mailcow, iRedMail, Mail-in-a-Box).
- Local development with Mailhog or Mailpit.
- Non-restrictive hosts (Hetzner, OVH).
Setup
import (
"github.com/gopackx/go-notification/channel/mail"
"github.com/gopackx/go-notification/channel/mail/smtp"
)
notifier.RegisterChannel(smtp.New(smtp.Config{
Host: "smtp.gmail.com",
Port: 587, // STARTTLS — leave TLS false; the driver upgrades automatically
Username: "user@gmail.com",
Password: os.Getenv("SMTP_PASSWORD"),
From: mail.Address{Name: "My App", Address: "noreply@example.com"},
}))TLS field = implicit TLS (port 465)
Set TLS: true only for implicit TLS on port 465. For STARTTLS on port 587, leave TLS: false — the driver upgrades the connection automatically when the server advertises STARTTLS. Setting TLS: true on port 587 will fail the handshake.
Gmail app password
Gmail won't accept your regular password for SMTP. You need an app password:
- Enable 2-Step Verification on your Google account.
- Go to https://myaccount.google.com/apppasswords.
- Create a new app password, label it e.g. "go-notification".
- Paste the 16-character password into
SMTP_PASSWORD.
Configuration reference
| Field | Type | Required | Description |
|---|---|---|---|
Host | string | yes | SMTP server hostname (e.g. smtp.gmail.com). |
Port | int | yes | 587 for STARTTLS, 465 for implicit TLS, 25 for plain. |
Username | string | no | SMTP auth username. Leave empty for no auth. |
Password | string | no | SMTP auth password or app password. |
AuthMechanism | string | no | "plain" (default) or "login". |
From | mail.Address | yes | Default sender. A struct: mail.Address{Name, Address}. |
TLS | bool | no | Implicit TLS (port 465). Leave false for STARTTLS on 587. |
InsecureSkipVerify | bool | no | Disable TLS cert verification. Never use in production. |
Timeout | time.Duration | no | Bounds the whole SMTP session — connect, TLS, every command through QUIT. Default: 30s. |
Name | string | no | Override the channel name (default "mail"). |
Timeouts and retries
Timeoutbounds the entire session, not just the TCP dial: connect, TLS handshake (implicit or STARTTLS),EHLO,AUTH,MAIL FROM,RCPT TO,DATAandQUIT. A server that accepts the connection and then stalls cannot hold a worker indefinitely. If thectxdeadline is earlier, it wins. With implicit TLS (TLS: true) the dial honoursctxcancellation as well.- 5xx replies are permanent.
550 mailbox unavailable,535 authentication failedand friends are wrapped inmiddleware.ErrPermanent: the notifier does not retry them and firesOnErrorright away. - 4xx replies are transient.
421 service not available,450 mailbox busy,451 local error(greylisting, rate limits) go through the normal retry schedule.
This is the reverse of the HTTP convention, where 4xx is permanent and 5xx is retried. SMTP's codes mean different things: 4xx is "try again later", 5xx is "don't bother".
Message format
A message with text, HTML and attachments is sent as nested MIME: a multipart/mixed envelope whose first part is a multipart/alternative (plain text + HTML), followed by one part per attachment. Mail clients render the HTML with the text fallback and list the attachments, instead of showing the body as a download. Text-only, HTML-only, and text+HTML without attachments use the simpler flat structure.
Troubleshooting
connection refused— wrong host/port, or firewall / cloud provider blocking.- handshake error on port 587 — you set
TLS: true. Leave itfalse; STARTTLS upgrades automatically. 535 5.7.8— bad credentials (permanent, not retried). For Gmail, you need an app password, not your account password.i/o timeoutmid-session — the server stalled after accepting the connection andTimeout(default 30s) expired. Raise it if your relay is genuinely slow.- Works locally, fails in prod — your cloud provider is almost certainly blocking port 25/587. Switch to an API driver.