May 27th - Blog Subscription Service - Part 1: The Why and the What
I wanted a way for readers to get notified when a new post drops. I looked at the SaaS options Mailchimp, ConvertKit, Buttondown, and they all work fine. But I'd be handing subscriber data to a third party and paying for a service I could build myself. So, I built it. This is the first in a series of posts covering how it works.
The Problem
The blog runs on Docusaurus, deployed to GitLab Pages. It already generates an RSS feed at /blog/rss.xml. What I needed was something to watch that feed and email subscribers when a new post drops, plus a way for people to sign up and manage their subscription.
The requirements were simple:
- Subscribers sign up with their email
- They get a confirmation email and click a link to opt in (double opt-in)
- When a new post drops, they get an email with a summary and a link
- They can unsubscribe at any time
No tracking pixels, no analytics, no third-party data sharing.
Why Not a SaaS?
The honest answer is that it's more interesting to build. But there are more legit reasons too:
- Cost - at small scale, the AWS services involved are essentially free. SaaS tools charge per subscriber or per send.
- Content - building it gave me something worth writing about.
The Architecture
The whole thing runs on AWS, managed with Terraform (I'm pretty new to mermaid so these should improve as time goes on) :
API Gateway (HTTP API) sits at api.mydomain.com and routes three endpoints to a single Lambda: POST /subscribe, GET /confirm, and GET /unsubscribe.
DynamoDB is a single table blog-subscribers with email as the partition key. Each item stores the email, a confirmed flag, and a UUID token used to validate confirmation and unsubscribe requests. A GSI(Global Secondary Index) on the confirmed attribute lets the poller query only confirmed subscribers efficiently.
SES handles all outbound email from a custom domain address. Domain identity, DKIM, and a custom MAIL FROM domain are all configured via Terraform.
EventBridge triggers the poller Lambda on a daily cron. The poller compares the RSS feed against a last_seen timestamp stored in DynamoDB and only sends emails for new posts.
What This Is Not
This isn't a marketing platform. There's no open tracking, no click tracking, and no segmentation. It's a plain-text (and HTML) email that says "new post dropped, here's the link." That's it.
What's Next
The next post covers the CI/CD setup. How GitLab authenticates to AWS without storing any credentials and how the permission model is structured to limit blast radius.
- Part 2: OIDC authentication and the Terraform CI pipeline
- Part 3: Terraform infrastructure deep dive
- Part 4: The Lambda Code
This is part of the Blog Subscription Service series.

Keep the coffee flowing