Email automation, tracking and dedicated server

Notes from a couple of years of building email tooling: from Python automation scripts inside a small communications team to running my own email server on a VPS for multiple independent projects.

Introduction

This page is a record of how I ended up doing email infrastructure work: what I built, why I built it, and what it taught me about this -sometimes given for granted- part of the internet.

I came into the domain rather by accident. My job title never said “email admin”, but the problems were engineering problems: deliver a thousand personalized messages reliably, know which ones were opened, keep the domain out of spam folders. Each question pulled me one layer deeper. The sections that follow move in rough chronological order, from the work that first made me curious to the infrastructure I now run.

Origin of my interest in email systems

I started paying attention to email seriously at ANIQ, around 2023 (check my résumé to catch up), where my role centered on communications and support materials for an industry association. A meaningful share of that work was sending information out (news, technical notices, promotions) to lists ranging from a few dozen to roughly a thousand recipients.

The first campaigns I sent used the usual mail-merge-and-upload approach. It worked, but barely: personalization broke in subtle ways, attachments got mangled, and I had no real visibility into what happened after a send.

What pulled me in was the gap between how simple email looks from outside and how much is going on underneath. Once I had a rough mental model of SMTP, MIME, and how receiving servers categorize messages, the day-to-day problems stopped looking like “the tool is annoying” and started looking like a system I could work with.

Automation in a real work environment

The first scripts were small and specific: open a CSV, format a subject line, attach a PDF, send through SMTP. Over time they grew: Jinja templating for per-recipient content, structured logging, and crucially, idempotent resumable sends so a mid-batch crash never meant double-mailing anyone. That last requirement shaped most of the design that followed.

Over my time at ANIQ I ran 114 campaigns through this homemade stack, each reaching roughly a thousand recipients with over 50% trackable open rate. The numbers were large enough that any small bug would have produced a visible failure. Building reliable tooling at that scale taught me more about correctness and recovery than larger volumes probably would have, and campaigns that used to consume entire afternoons became near-background tasks.

     114 campaigns

  ~1_000 recipients per send

~114_000 messages dispatched

Custom tracking implementation

Once sending was reliable, the obvious next question was whether anyone actually read these. Hosted trackers were either too expensive, too restrictive on volume, or required handing our list to a third party. So I wrote my own.

The mechanism is the same one every commercial tracker uses: each message embedded a transparent pixel pointing at an endpoint I controlled, and every link was rewritten through a redirect on the same server.

<img
  src="{API}/pixel_tracking/{UUID}.png"
  width="1"
  height="1"
  alt=""
>

The endpoint was a thin Python service writing event type with Flask, timestamp, recipient ID, and campaign ID to a database table. Here I put a simplified code of the behavior:

[email recipient] -> Requests <img> from server
 |
 |
 |
[API Server] -> /pixel_tracking -> store open in MySQL -> return send_file("pixel.png")

Just like that. No JavaScript, no fingerprinting, no third-party calls. A handful of SQL queries produced open and click reports per campaign, which was enough to make real editorial decisions about subject lines, send times, and content.


UUID-based email and link tracking

The detail that made the tracker robust was using a fresh UUID for every recipient and every link, rather than sequential IDs or the email address itself. The mapping from UUID to recipient lived in the database, never in the URL.

<p>
  Check <a href="{API}/click_tracking/?token_url={TOKEN}&id={RANDOM_UUID}">
    this
  </a> out
</p>
[email recipient] -> Clicks <a>
 |
 |
 |
[API Server] -> /click_tracking -> store click in MySQL -> retrieve actual URL by token -> return redirect(url)

This removed a whole class of problems before they happened. Sequential IDs would have been trivially scrapeable and anyone could have enumerated the list or triggered fake opens. Putting the email address in the URL would have leaked it through forwarded messages. UUIDs avoided both, and because they were opaque, I could share aggregated reports without exposing anything sensitive.

Per-link UUIDs also let me attribute a click to a specific link in a specific message to a specific person.

Email marketing automation

With sending and tracking in place, the rest of the work was turning the system into something operable. A campaign was defined by a single YAML file — subject, HTML filename for the body, recipient query, send schedule. A scheduler respected throttle limits so I would not get rate-limited by receiving servers.

Throttling is the single thing that separates a working bulk mailer from one that gets a domain flagged. I capped sends per minute, retried transient errors with backoff, and treated certain SMTP codes as permanent failures that removed the address from future sends.

Bounces were parsed and recorded so lists stayed clean automatically. None of this was novel, every ESP does the same. But by doing it myself, I actually understood every step and could fix or extend any of it.

Self-hosted Mailcow infrastructure

Some time after leaving that role (2025) I hit the same problem from a different angle: several personal and freelance projects each needed real email, and buying mailboxes from a provider for every one was disproportionate. So I deployed my own server.

I chose Mailcow because it bundles what I would have stitched together anyway: Postfix, Dovecot, Rspamd, SOGo, all wrapped in a reproducible Docker Compose stack. The initial deployment was the easy part. The interesting work came after:

  • reviwing DMARC reports
  • Rspamd tunning
  • setting up a landing page on WordPress without disrupting the rest of the system
  • clean Let’s Encrypt renewal.

Running my own server has a clarifying effect: every issue is mine. There is no support queue to escalate to, which forces you to actually learn the system.

Domain management and deliverability

The biggest lesson from running this is that getting mail to the inbox has little to do with the mail server itself and almost everything to do with the records around it.

Every domain I send from has SPF, DKIM with a strong key, and DMARC starting at p=none while reports come in and tightening once the picture is clean. Reverse DNS has to match the HELO name and resolve forward. MX has to point where mail is actually accepted. The TLS cert on port 25 has to be valid for the announced hostname. Any one of these being wrong quietly costs deliverability.

Deliverability is also a relationship over time, not a configuration. New IPs need warming. Engagement signals (opens, replies, lack of complaints) are what move reputation. None of this is an on/off setting, the only way to learn it is to send real mail and watch. I track DMARC aggregate reports for every domain I operate, which keeps the infrastructure honest.

Lessons learned

  • Email feels more political than technical. The protocols are well-documented and the software is good. The hard part is convincing a handful of large mailbox providers that your messages are wanted, and that is a relationship maintained over time, not a problem solved once.
  • Observability beats cleverness. Most of the time I have spent fixing email problems has been spent not knowing what was happening. Once I had logs, queue visibility, DMARC reports, and seed inboxes to test against, the problems became tractable.
  • “Communications job” and “engineering job”. Being both the person who writes the email and the one who handles the technical aspects of sending it is the task that has taught me the most useful skills in recent years.