From-Scratch Build · Backend Services
A self-hosted mailing list. Send a message to a single list address and a background service quietly fans it out to everyone subscribed — handling sign-ups, unsubscribes and multiple lists, all from one mailbox. Built from scratch to learn how email actually moves under the hood.
What it is
The service runs your own group mailing lists. Each list has an alias — a single address people can write to. A long-running daemon watches the mailbox over IMAP, and whenever a message arrives for a list alias, it re-sends that message over SMTP to every active subscriber of that list. Reply to the group, and everyone gets it, without anyone needing a third-party platform.
I built this because email feels like magic until you implement it. I wanted to see, end to end, how a server reads new mail, decides who a message belongs to, and relays it on — and how subscribe and unsubscribe requests fit into the same loop.
The core idea I wanted to learn: a mailing list is just a polling loop with a routing table. Watch one mailbox, match the recipient alias against a list of subscribers, and forward — the whole behaviour falls out of "read, look up, send", done reliably and on a schedule.
The stack
This rebuild is a small backend that touches the raw email protocols as well as a web framework. Here is what each piece does.
Holds the data model and admin — lists and subscribers — plus the management command that launches the daemon.
The protocol for reading a mailbox. The daemon polls for new, unseen messages to process.
The protocol for sending mail. Each matched message is relayed out to every subscriber on the list.
Instead of a password, the daemon authenticates to the mail provider with a refreshable access token.
Mailing lists and subscribers, with a many-to-many link so one person can belong to several lists.
A container runs the web app and the daemon side by side, kept alive by a process supervisor.
Architecture
The heart of the system is one resilient loop. It wakes on a schedule, drains anything new, and goes back to sleep — simple enough to reason about, sturdy enough to leave running.
Refresh the OAuth access token and open IMAP and SMTP connections to the provider.
Check the mailbox for unseen messages that have arrived since the last pass.
Read each message's recipient and match it against a known mailing-list alias.
Re-send the message over SMTP to every active subscriber of the matched list.
Process subscribe and unsubscribe requests, updating the list and confirming by email.
Record the checkpoint, wait the polling interval, and run the loop again.
How it runs
Around the loop sits the management layer that makes the service usable rather than just functional:
In my rebuild I focused on making the loop dependable — token refresh, a last-checked checkpoint, and clean reconnects are what let it run unattended for days.
Reflection