This project is a bulk email sender written in Go, designed to send personalized emails to multiple recipients concurrently using goroutines and channels. It uses a template system to customize email content per recipient and is tested with the Mailpit SMTP server running in Docker.
- Reads recipients from a CSV file (
email.csv) - Uses HTML templates (
email.tmpl) for personalized emails - Concurrent email sending with multiple workers (goroutines)
- Channels for safe communication between producer (CSV loader) and consumers (email workers)
- Tested with a local SMTP server (Mailpit)
go-bulk-mailer/
├── main.go # Entry point; sets up producer-consumer pipeline
├── producer.go # Loads recipients from CSV and pushes to channel
├── consumer.go # Reads recipients from channel and sends emails
├── email.tmpl # Email template with placeholders for recipient data
├── email.csv # Sample CSV with recipient Name and Email
└── README.md
- Go 1.20+ installed
- Docker installed (for Mailpit SMTP server)
- Basic knowledge of Go and SMTP
Mailpit is a local SMTP testing server. It allows you to catch emails sent from your app without sending them to real addresses.
- Pull and run Mailpit Docker image:
docker run -d -p 1025:1025 -p 8025:8025 mailpit/mailpit- SMTP Port:
1025(for sending emails) - Web UI:
http://localhost:8025(to view captured emails)
- Open your browser at
http://localhost:8025to view emails sent by the application.
Emails sent using this server won’t reach real inboxes, making it safe for testing.
- Clone this repository:
git clone <repo-url>
cd go-bulk-mailer- Create a CSV file named
email.csvwith the following format:
Name,Email
John Doe,john@example.com
Jane Smith,jane@example.com-
Ensure
email.tmplis present in the project root with your desired template. -
Run the application:
go run main.go producer.go consumer.go- Open
http://localhost:8025in your browser to see the sent emails.
Goroutines are lightweight threads managed by Go runtime. They allow concurrent execution, making it possible to send multiple emails in parallel.
Example in main.go:
var wg sync.WaitGroup
workerCount := 5
for i := 1; i <= workerCount; i++ {
wg.Add(1)
go emailWorker(i, recipientChannel, &wg) // <-- Goroutine for each worker
}
wg.Wait() // Wait for all goroutines to finish- Each
emailWorkerruns concurrently, processing emails independently.
Channels are used for safe communication between goroutines. An unbuffered channel blocks senders until a receiver is ready, ensuring synchronization.
Example in producer.go and main.go:
recipientChannel := make(chan Recipient) // Unbuffered channel
go func() {
loadRecipient("./email.csv", recipientChannel) // Producer sends data
}()
for recipient := range recipientChannel { // Consumer reads data
fmt.Println("Recipient:", recipient.Name)
}- Producer (
loadRecipient) sends recipients to the channel. - Consumers (
emailWorker) receive recipients from the channel.
sync.WaitGroup ensures the main program waits for all workers to finish before exiting.
Example in main.go:
var wg sync.WaitGroup
wg.Add(1) // Add a worker
go emailWorker(i, recipientChannel, &wg)
wg.Wait() // Wait for all workers to finishIn consumer.go:
defer wg.Done() // Signals that this worker is finishedhtml/template is used to generate personalized emails dynamically.
Example in main.go:
func executeTemplate(recipient Recipient) (string, error) {
t, err := template.ParseFiles("email.tmpl") // Load template
if err != nil {
return "", err
}
var tpl bytes.Buffer
err = t.Execute(&tpl, recipient) // Execute template with recipient data
return tpl.String(), err
}- Templates can include variables like
{{.Name}}and{{.Email}}. - This allows each recipient to get a customized email.
net/smtp is used to send emails over the SMTP protocol.
Example in consumer.go:
err := smtp.SendMail(
smtpHost+":"+smtpPort,
nil,
"vivekbargude@gmail.com",
[]string{recipient.Email},
[]byte(msg),
)- In this project, Mailpit (
localhost:1025) is used for testing instead of sending real emails.
- Adjust
workerCountinmain.goto change concurrency. - Ensure CSV and template files exist in the project root.
- For production, replace Mailpit with a real SMTP server (Gmail, SendGrid, AWS SES, etc.)