Post

Sending Bulk Emails with Golang using SMTP

Build a bulk email sender in Go with SMTP, HTML support, and custom reply-to. Complete tutorial with code examples and environment setup.

Sending Bulk Emails with Golang using SMTP

In this tutorial, we’ll build a Go program that sends bulk emails through SMTP with custom reply-to addresses and rich HTML content. We’ll use Go’s built-in net/smtp package alongside github.com/joho/godotenv for managing environment variables securely.

You can find the complete source code in my GitHub repository.


🚀 Prerequisites

Before we begin, make sure you have:

  • Go installed on your system
  • Access to an SMTP server (Gmail, SendGrid, etc.)
  • Basic understanding of Go syntax

📦 Setting Up Dependencies

First, let’s install the required package for environment variable management:

1
go get github.com/joho/godotenv

🔧 Configuration Setup

Create a .env file in your project root to store sensitive SMTP credentials:

1
2
3
4
5
6
7
8
9
10
11
12
# Rename this file to .env 

# SMTP credentials
SMTP_USERNAME=""
SMTP_PASSWORD=""

# SMTP server information
SMTP_HOST=""
SMTP_PORT=""

# Sender's email address
FROM_EMAIL=""

Important: Never commit your .env file to version control. Add it to your .gitignore file.


💻 Implementation

Now let’s create our main Go file with the bulk email functionality:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package main

import (
    "fmt"
    "github.com/joho/godotenv"
    "log"
    "os"
    "net/smtp"
)

func main() {
    // Loading environment variables
    err := godotenv.Load(".env")
    if err != nil {
        log.Fatalf("Error loading environment variables file")
    }

    // SMTP server credentials from .env file
    SMTP_USERNAME := os.Getenv("SMTP_USERNAME")
    SMTP_PASSWORD := os.Getenv("SMTP_PASSWORD")
    SMTP_HOST := os.Getenv("SMTP_HOST")
    FROM_EMAIL := os.Getenv("FROM_EMAIL")
    SMTP_PORT := os.Getenv("SMTP_PORT")

    log.Println("SMTP credentials initialized for:", SMTP_USERNAME)

    // Setup authentication
    auth := smtp.PlainAuth("", SMTP_USERNAME, SMTP_PASSWORD, SMTP_HOST)

    // List of recipient emails
    toList := []string{"recipient1@gmail.com"}
    // For multiple recipients:
    // toList := []string{"email1@gmail.com", "email2@gmail.com", "email3@gmail.com"}

    // Email content
    subject := "Test Golang Program"
    body := "Hello, this is an HTML-rich email template!"
    
    // Custom reply-to email (optional)
    reply_to := ""
    if reply_to == "" {
        reply_to = FROM_EMAIL
    }

    // Construct email message with HTML support
    var msg []byte
    msg = []byte(
        "From: " + FROM_EMAIL + "\r\n" +
        "Reply-To: " + reply_to + "\r\n" +
        "Subject: " + subject + "\r\n" +
        "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\r\n" +
        "\r\n" +
        body + "\r\n")

    // Send the email
    err = smtp.SendMail(SMTP_HOST+":"+SMTP_PORT, auth, FROM_EMAIL, toList, msg)

    // Handle any errors
    if err != nil {
        log.Println("Failed to send email:", err)
        os.Exit(1)
    }

    fmt.Println("✅ Successfully sent emails to all recipients!")
}

🏃‍♂️ Running the Program

Execute your program with:

1
go run .

If everything is configured correctly, you should see a success message indicating that emails were sent to all recipients.


🎨 Customization Options

For Plain Text Emails

If you prefer plain text over HTML, replace the message construction with:

1
2
3
4
5
msg = []byte(
    "Reply-To: " + reply_to + "\r\n" +
    "Subject: " + subject + "\r\n" +
    "\r\n" +
    body + "\r\n")

Adding More Recipients

Simply expand the toList slice:

1
2
3
4
5
toList := []string{
    "user1@example.com",
    "user2@example.com",
    "user3@example.com",
}

🛡️ Best Practices

  • Rate Limiting: Most SMTP providers have sending limits. Consider adding delays between emails for large batches.
  • Error Handling: Implement retry logic for failed sends.
  • Security: Never hardcode credentials. Always use environment variables.
  • Validation: Validate email addresses before sending.

🎯 Conclusion

You’ve successfully built a Go program that can send bulk emails with HTML content and custom reply-to addresses. This foundation can be extended to include templates, better error handling, and more sophisticated email features.

The combination of Go’s simplicity and the power of SMTP makes email automation straightforward and efficient. Feel free to customize this code for your specific use cases—whether it’s newsletters, notifications, or marketing campaigns.

Happy coding! 🚀

This post is licensed under CC BY 4.0 by the author.