Send email with Go (net/smtp) over SMTP
Go SMTP example using the standard library: smtp.PlainAuth, STARTTLS on 587 via smtp.Dial + StartTLS (or tls.Dial for 465), a properly formatted MIME message, and the go-mail alternative for attachments.
Install
shell
# standard library — or: go get github.com/wneessen/go-mail (attachments, HTML, retries)
Complete example
Go — STARTTLS on 587, credentials from the environment
package main
import (
"crypto/tls"
"fmt"
"net/smtp"
"os"
"strings"
)
func main() {
host := getenv("SMTP_HOST", "smtp.queensmtp.com")
port := getenv("SMTP_PORT", "587")
user, pass := os.Getenv("SMTP_USER"), os.Getenv("SMTP_PASS")
from, to := "hello@yourdomain.com", "customer@example.com"
msg := strings.Join([]string{
"From: Your App <" + from + ">",
"To: " + to,
"Subject: Your receipt",
"MIME-Version: 1.0",
"Content-Type: text/html; charset=UTF-8",
"",
"<h1>Thanks for your order</h1><p>#1042 is confirmed.</p>",
}, "\r\n")
auth := smtp.PlainAuth("", user, pass, host)
tlsCfg := &tls.Config{ServerName: host}
var c *smtp.Client
var err error
if port == "465" { // implicit TLS
conn, e := tls.Dial("tcp", host+":"+port, tlsCfg)
if e != nil { panic(e) }
c, err = smtp.NewClient(conn, host)
} else { // STARTTLS
c, err = smtp.Dial(host + ":" + port)
if err == nil { err = c.StartTLS(tlsCfg) }
}
if err != nil { panic(err) }
defer c.Quit()
if err = c.Auth(auth); err != nil { panic(err) }
if err = c.Mail(from); err != nil { panic(err) }
if err = c.Rcpt(to); err != nil { panic(err) }
w, err := c.Data()
if err != nil { panic(err) }
fmt.Fprint(w, msg)
w.Close()
fmt.Println("sent")
}
func getenv(k, d string) string { if v := os.Getenv(k); v != "" { return v }; return d }What to know
- smtp.SendMail() also works for 587 (it performs STARTTLS when offered) but gives no control over TLS config or timeouts; the explicit client above does.
- PlainAuth refuses to send over an unencrypted connection — a good safety net.
- For attachments and multipart bodies use github.com/wneessen/go-mail or jordan-wright/email rather than hand-building MIME.
Same code, different provider
Only the host, port and credentials change. The rules per provider are on the SMTP settings hub.
| Provider | Host | Port | Username | Password | Note |
|---|---|---|---|---|---|
| QueenSMTP | smtp.queensmtp.com | 587 (STARTTLS) / 465 (SSL) / 2525 | SMTP username from the dashboard | SMTP password | $5/year + $0.10 per 1,000; 100 free/day |
| Gmail | smtp.gmail.com | 587 / 465 | you@gmail.com | App Password | 500/day, From must be the account |
| Amazon SES | email-smtp.<region>.amazonaws.com | 587 / 465 / 2587 | SES SMTP username | SES SMTP password | sandbox 200/day until approved |
| Office 365 | smtp.office365.com | 587 | user@yourdomain.com | mailbox password | SMTP AUTH must be enabled; 30/min |
| SendGrid | smtp.sendgrid.net | 587 / 465 / 2525 | apikey | API key | no free plan since 2025 |
Credentials for the example above
Create a free account, verify your sending domain (SPF + DKIM shown in the dashboard), add an SMTP credential, and paste it into the code. 100 emails a day free; $5/year plus $0.10 per 1,000 after that. Prefer HTTPS? The same account has a REST email API.
Get SMTP credentials Test your settings firstOther languages and frameworks
PHPsend email over SMTP
Laravelsend email over SMTP
WordPresssend email over SMTP
Pythonsend email over SMTP
Djangosend email over SMTP
Flasksend email over SMTP
Node.jssend email over SMTP
Next.jssend email over SMTP
Javasend email over SMTP
C# / .NETsend email over SMTP
Ruby on Railssend email over SMTP
Postfix (Linux relayhost)send email over SMTP
curl / Bashsend email over SMTP
Go SMTP — questions
PlainAuth requires TLS. Call StartTLS on 587 before Auth, or connect with tls.Dial on 465.
Related Guides
Continue learning with these related articles