Blog · 17 July 2026 · Maarten DraijerHow we built email.eu · part 5 of 7

Infrastructure you can rebuild from one repository

One Kubernetes cluster, a handful of standalone VMs, everything in OpenTofu, and deploys that are git commits. Why the unfashionable half of our design is deliberate, and four infrastructure rules that hold with no exceptions.

There is a genre of infrastructure blog post that lists a service mesh, three observability vendors, a multi-cloud failover story and a platform team, and ends with "and that's how we serve 4,000 users". This is not that post. Our infrastructure is designed to be boring, and the boredom is load-bearing. Part 5 of the series.

Everything runs at OVHcloud in France, which part 1 explains wasn't an accident. Within that, the platform has exactly two shapes.

Two shapes of compute

A Kubernetes cluster runs the coordination layer: the control plane at app.email.eu, the public website, the status page, our metrics and probes, and the scheduled jobs that keep the platform tidy. These are the components we deploy often, and the cluster gives us rolling updates, self-healing and easy horizontal scale where we actually need them. Alongside them it runs our Postgres databases, managed by an operator, with replicas and a connection pooler, which part 4 covered from the failure side.

Standalone virtual machines run the heavyweight pillars: mail, file storage, chat, meetings, document editing. This is the unfashionable half of the design, and it's deliberate. These products are stateful, disk-hungry, and want stable network identities; a mail exchanger's IP address is part of its reputation. They update on their own upstream cadence, not ours. And keeping them on separate machines keeps failures separate: a bad day for the file server is physically incapable of starving the mail server of memory. Kubernetes buys its magic with a layer of indirection we'd rather not put under a mail spool.

The test we apply is simple. Does this component benefit from being rescheduled automatically, or would we rather know exactly which machine it's on when something's wrong at 3am? Web things pass the first test. Mail passes the second.

Everything is code

The entire estate, cluster, VMs, DNS, load balancers, is declared in OpenTofu, the open-source infrastructure-as-code tool, in one repository. The VMs configure themselves from idempotent scripts, so "set up a mail node" is not a wiki page of steps, it's a script that produces the same result every time and can be re-run safely. If a machine died unrecoverably, we would recreate it from the repository rather than from memory.

Secrets follow the same principle with one twist: they live encrypted in the repository (age encryption, keys held by humans, never plaintext in git), and the deploy tooling refuses to apply anything if the local secrets have drifted from the encrypted source of truth. That guard exists because "which laptop has the right variables" is how teams have big accidents.

Deploys are git commits. Our self-hosted CI at ci.email.eu (the EU-suppliers rule applies to build infrastructure too) builds every change into an image tagged with the exact commit that produced it, and those tags are immutable: a tag can never quietly start meaning something else. But building is not shipping. Nothing reaches production because code was merged. A deploy is its own one-line commit, written and pushed by a human, that changes which image production runs; the pipeline applies exactly that pin and waits until the rollout is healthy. This kept the two properties we cared about when deploys were a person running a script: every deploy is a deliberate human decision, and the deploy history is just git history, so rolling back is reverting a commit.

Rules we follow

None of these rules is exotic. Their value is that they're absolute: they hold when you're tired, when you're sure, and when you're in a hurry, which is exactly when they're needed.

Why a customer should care

Because boring compounds in your favour. Fewer moving parts means fewer novel failure modes, and a platform reconstructable from one repository means our disaster story doesn't depend on anyone's memory. When a provider dazzles you with architecture, it's fair to ask who's paying for the excitement. Our answer is that nobody should, so we removed it.

Next, part 6: how we watch all of this, why our status page shows the same numbers we alert on, and what we've built for the days when something breaks anyway.