Euromailing bouwen: waarom we onze eigen MTA draaien in plaats van een ESP door te verkopen
Hoe we een AVG-native e-mailmarketingplatform bouwden op Rails 8.1 en KumoMTA, en waarom het bezit van de verzendlaag de architectuur van het hele product bepaalt.
De meeste e-mailmarketingplatformen zijn een interface om andermans mailserver heen. Je maakt een account, bouwt een lijst, ontwerpt een campagne — en zodra je op verzenden drukt, gaat je bericht naar SendGrid, Mailgun of Amazon SES. Het platform waarvoor je betaalt, is zelf huurder op infrastructuur die het niet beheert.
Dat model werkt tot het niet meer werkt. Toen we Euromailing begonnen te bouwen, besloten we de verzendlaag zelf in handen te nemen. Deze post gaat over wat die keuze kost, wat hij oplevert, en hoe hij een Rails-applicatie vormgeeft.
De doorverkoopval
Verkoop je een ESP door, dan zijn er drie dingen waar en geen daarvan heb je in de hand.
Je deliverability is de reputatie van iemand anders. Je deelt IP-pools met elke andere klant van die provider. Een spammer drie accounts verderop kan jouw inboxplaatsing verslechteren, en het enige wat je kunt doen is een ticket indienen.
Je marge ligt vast in hun prijslijst. Je koopt groothandel in en verkoopt retail, en dat verschil is je bedrijf. Verhogen zij hun prijs, dan wordt jouw product slechter of je marge verdwijnt.
Je data passeert de grenzen die zij kiezen. Dit is in Europa de belangrijkste. Als je klanten Nederlandse verenigingen, Duitse bureaus of Franse retailers zijn, kan “AVG-conform” niet betekenen “we hebben een verwerkersovereenkomst met een Amerikaanse partij getekend en er het beste van gehoopt”. Contactgegevens, open-tracking en bouncelogs zijn persoonsgegevens.
De MTA zelf beheren haalt alle drie die beperkingen weg en vervangt ze door één veel zwaardere: je moet nu goed zijn in het draaien van mailinfrastructuur.
KumoMTA als verzendlaag
Het daadwerkelijke verzenden doen we met KumoMTA, een open source MTA voor grootverzenders. De eigenschap die voor ons doorslaggevend was: het legt precies datgene bloot waarover een marketingplatform moet kunnen redeneren — wachtrijen, traffic shaping per domein, bounceclassificatie — in plaats van het te verstoppen achter een API.
De Rails-applicatie praat nooit rechtstreeks SMTP met ontvangers. Ze stelt samen, ze beslist, en ze draagt over. KumoMTA doet de onderdelen die jaren aan operationele kennis vereisen: hergebruik van verbindingen, retry-backoff, throttling per provider, TLS-onderhandeling.
Die taakverdeling houdt de Rails-kant begrijpelijk:
- Rails bezit de intentie. Wie zou dit moeten ontvangen, is diegene nog steeds ingeschreven, respecteert deze campagne hun voorkeuren, hoe ziet het gerenderde bericht eruit.
- KumoMTA bezit de bezorging. Hoe snel nu naar Gmail sturen, wat te doen met een 421, wanneer opgeven.
- De terugkoppeling is van allebei. Bounces, klachten en suppressies stromen terug naar Rails, waar ze domeingebeurtenissen worden die volgende verzendingen beïnvloeden.
Dat laatste punt is waar de meeste doorverkoopplatformen dun worden. Beheert de ESP de bounce-afhandeling, dan krijg je een webhook en een statusstring. Beheer je het zelf, dan classificeer je een harde bounce op de werkelijke SMTP-respons, bepaal je je eigen suppressiebeleid, en kun je een klant precies uitleggen waarom een adres geen post meer ontvangt.
Multi-tenancy zonder lekken
Euromailing is multi-tenant: veel bedrijven, elk met eigen contacten, templates, campagnes en API-sleutels. In een e-mailplatform is een tenancy-lek geen ongemak — het is de campagne van de ene klant versturen naar de lijst van de andere.
We behandelen tenant-scoping als iets wat onmogelijk te vergeten moet zijn in plaats van iets wat ontwikkelaars onthouden. Elk querypad dat contactdata raakt, wordt gescoped onder de controllerlaag, zodat een ontbrekende where in een nieuwe feature de schade niet kan vergroten. Dat is dezelfde klasse probleem als in multi-tenancy-strategieën voor Rails en row-level security in Postgres.
De publieke API maakt dit scherper. Sleutels zijn scoped en rate limited, per account uitgegeven, en exact één keer zichtbaar bij aanmaak:
curl https://euromailing.com/api/v1/contacts \
-H "Authorization: Bearer eml_live_..."
Een API is een belofte dat anderen op jouw datamodel gaan bouwen, dus de tenant-grens moet standhouden onder toegangspatronen die je niet hebt ontworpen.
MJML, want HTML-mail is geen HTML
Wie ooit met de hand een responsive e-mailtemplate heeft geschreven, weet dat e-mailclients een fossielenbestand aan HTML implementeren. Outlook wil nog steeds tabellen. Gmail strips van alles. Dark mode keert om wat het wil omkeren.
Templates in Euromailing worden in MJML geschreven en gecompileerd naar de tabellensoep die e-mailclients daadwerkelijk accepteren. Die compilatie zit in het buildpad, niet in het handwerk van een marketeer, waardoor een templatewijziging net zo reviewbaar is als elke andere wijziging.
Een kleine beslissing die een hele categorie supporttickets wegneemt.
Workflows zijn state machines, geen cronjobs
Met de visuele workflowbouwer beschrijft een klant automatisering: als iemand zich inschrijft, wacht twee dagen, stuur het welkomstbericht, en als er geklikt is, plak er een label op.
De naïeve implementatie is een geplande job die elke paar minuten alle contacten langsloopt op geschiktheid. Dat werkt bij tienduizend contacten en bezwijkt bij tien miljoen.
In plaats daarvan krijgt een contact dat een workflow binnenkomt een positie in die workflow, en worden overgangen individueel ingepland. Het werk is evenredig aan het aantal contacten dat daadwerkelijk beweegt, niet aan de omvang van de database. Solid Queue verzorgt de planning, met Redis eronder voor de rate limiter.
Inkomende mail is geen bijzaak
De meeste platformen behandelen e-mail als eenrichtingsverkeer, waardoor het reply-to-adres uitkomt op een plek die niemand leest. Euromailing routeert inkomende mail: antwoorden, bounces die als bericht binnenkomen in plaats van als SMTP-respons, en afmeldingen die mensen als gewone tekst sturen omdat ze jouw voorkeurencentrum toch nooit vinden.
Inkomende mail netjes routeren is wat een campagne laat aanvoelen als correspondentie in plaats van als een uitzending.
Wat het zelf beheren werkelijk kost
Ik wil eerlijk zijn over de afweging, want “draai je eigen infrastructuur” is advies dat veel te gemakkelijk gegeven wordt.
Je neemt operationele verantwoordelijkheid op je die nooit slaapt. IP-warming is echt en traag. Blocklists bewaken is werk. Besluit Microsoft op een zondag dat jouw ranges verdacht ogen, dan is dat jouw zondag. Een doorverkoper betaalt marge om die problemen van iemand anders te maken, en voor veel bedrijven is dat de juiste ruil.
Het is de juiste keuze wanneer deliverability het product ís, wanneer dataopslag in Europa een wettelijke eis is en geen voorkeur, en wanneer je de operationele diepgang hebt om het te dragen. Voor een Europees platform dat aan Europese bedrijven verkoopt, gold alle drie.
Het Rails-deel is bijna saai
De stack is bewust onspannend: Rails 8.1, PostgreSQL, Solid Queue, Redis, Tailwind. RSpec en FactoryBot voor de testsuite. Niets hiervan is nieuw, en dat is precies de bedoeling — alle interessante risico’s van dit product zitten in de mailinfrastructuur en het tenancy-model, dus de applicatielaag hoort het meest voorspelbare van het hele gebouw te zijn.
Zo bouwen we bij TTB Software in het algemeen: besteed je nieuwigheidsbudget waar de werkelijke moeilijkheid zit, en laat de rest de saaie, goed gedocumenteerde optie zijn die elke Rails-ontwikkelaar in een middag oppakt.
Iets aan het bouwen waar deliverability, AVG of multi-tenancy een eerste zorg is in plaats van een vinkje? TTB Software doet Rails-architectuur en fractional CTO-werk. Negentien jaar ervaring met dit soort systemen.
Related Articles
Ledenboek bouwen: verenigingsbestuur vastleggen in Rails
Hoe we een ledenadministratieplatform voor Nederlandse verenigingen bouwden, en waarom het moeilijke deel niet de CRU...
Rails 2FA (TOTP): Twee-factor-authenticatie met ROTP, back-upcodes en versleutelde secrets
Rails 2FA met TOTP: implementeer twee-factor-authenticatie met ROTP, genereer back-upcodes, versleutel secrets met Ac...
Rails Idempotency Keys: Veilige API-retries, Stripe-stijl, voor Betalingen en Webhooks
Rails idempotency keys: maak POST-endpoints veilig te retryen. Stripe-stijl middleware, Postgres-opslag, TTL, race-co...