For the last while I've been collecting interesting articles, videos and music albums that I've liked into a newsletter. As a next step, I decided that I'd like to start emailing this newsletter out to a list of subscribers.
Going down the more challenging path that I always follow, I decided to roll my own system as a learning experience.
I took inspiration for my newsletter's design from the Microsoft.Source newsletter from the Microsoft Azure team.
Making a newsletter
Content for my newsletter is managed through Hugo like the rest of my site. Hugo also provides nice utilities like
.Previous links to aid readers navigating between newsletter issues.
There are a few caveats that come with sending emails as opposed to hosting web pages though.
- CSS must be embedded within
<style/>elements or inlined, as clients can ignore
- Absolute URLs must be used, as links with only a path (like
/blog/hello-world) end up broken.
With the help of Hugo shortcodes, I can write content for my newsletter while leaving styling/layout to the templating system.
Not the prettiest design, but it gets the job done now and I can improve it later!
Sending emails with Mailgun
With a newsletter ready to send, I needed a mechanism to deliver emails.
I went with Mailgun, since they have predictable billing (US$0.80 per 1000 emails) and several nice features to manage mailing list subscribers. There are a plethora of providers to choose from though, so always do your own investigating!
Here's the script that sends out my newsletter to all the subscribers of the
newsletter\@mailgun.nicholas.cloud mailing list in Mailgun. This mailing list address only accepts messages by authenticated API calls, so I don't need to worry about it being hit with spam from the outside world.
curl --fail "https://nicholas.cloud/newsletter/2020-03/" > email.html curl --fail "https://templates.mailchimp.com/services/html-to-text/" --data-urlencode [email protected] > email.text curl --fail "https://api.eu.mailgun.net/v3/mailgun.nicholas.cloud/messages" \ --user "api:$MAILGUN_API_KEY" \ --data-urlencode to=[email protected] \ --data-urlencode "from=Nicholas <[email protected]>" \ --data-urlencode "subject=Newsletter #2020-03 from nicholas.cloud" \ --data-urlencode [email protected] \ --data-urlencode [email protected]
To improve the delivery of emails, it's important to include a plaintext counterpart for the HTML content of the email. Conveniently, the folks at Mailchimp have a tool to generate this email text. Better yet, it can be
Once the messages have been accepted by Mailgun, I can sit back and wait for them to be delivered! Mailgun manages the sending here, and even adds an unsubscribe link for me through their templating system!
Automating releases with Buildkite
With an existing pipeline in Buildkite for my website, adding a publishing step for a newsletter seemed reasonable.
I added a new step after deploying that would check for new newsletter issues (thanks
git diff-tree!), and add the necesarry email-sending steps to the in-progress build!
Buildkite supports dynamic pipelines, so adding these steps while the build is already running is no worry at all!
With the aid of a block step I can send a preview email to myself first, and only send it on to my subscribers once I've unblocked it in Buildkite!
Lastly, while I might have a pipeline to send my newsletters out, I'm still without subscribers to send to! A little bit more than a plain form to grab emails is needed here though.
I made a Cloudflare Worker for this, since I wanted to deploy a small-ish application a particular route on my website (being
After a new reader supplies their email to a (very) basic form, I send an email to confirm the subscription. This double opt-in approach helps ensure all my subscribers are genuine, but it introduces additional complexity into the subscription process. How do you make sure the confirmation is genuine?
My solution was to send pending subscribers a confirmation link that included a signed representation of their email. That way, a subscription can only be confirmed with this signature, and until then I won't send the subscriber any contact. The WebCrypto API available to workers made this a breeze to implement.
With the processes in place to publish and deliver newsletters, all my subscribers need to do is to sign up and wait to hear from me!
I hope you've enjoyed reading about how I set up my newsletter mailing list!
You're more than welcome to check out my newsletter, and if you'd like to subscribe you can to receive it in your email inbox too!
If you're interested in seeing all the code powering this, you can check out the repo of my website and the worker for accepting subscriptions!
Got questions or want to discuss something? Leave me a comment!