A medical conference needed a sign-up page. People fill in their details, the details get saved, and the organisers can follow up afterwards. A small, self-contained job.
The usual way to build this is to go shopping. You rent a form service, rent a place to store the entries, rent an email tool to send the follow-ups. Three separate companies, three monthly bills, three logins to keep track of — and every one of those bills keeps arriving whether a single person signs up or not.
We asked a different question. What if we build the whole thing without renting anything new at all?
We already owned the workshop
We already run servers for our other work. They were paid for, switched on, and sitting there with spare room. So instead of renting a new place to put the sign-up form, we put it on the machines we already had.
Think of it like a builder who already owns a fully kitted workshop. A neighbour asks for a single bookshelf. The builder does not rent a second workshop to make one shelf — they walk into the one they already have, use the tools already on the bench, and the only real cost is the wood. Everything else was paid for long ago.
That is what the conference sign-up form cost us: a little bit of "wood." The expensive part — the building, the tools, the keeping-the-lights-on — was already covered by the work that justified buying it in the first place.
Why not just give every client their own everything?
There is a genuine argument for renting a fresh, separate setup for each client. It keeps them completely walled off from one another. If one client leaves, you hand them their box and wave goodbye. If something breaks, it breaks for one client and no one else.
Those are real benefits and we take them seriously. But for a single conference that happens once and is then over, a dedicated setup is mostly cost and hassle for very little return. You would build the thing, look after it, keep it patched and safe, and then switch it off three months after the event ends. For one sign-up form, that is a lot of effort for almost no gain.
Sharing what we already run flips the sums around. The big costs were already paid. The conference just moves in as a tenant — like a flat in a building that already exists — and the rent on that one flat is tiny.
The catch, and how you make it safe
Sharing comes with a real risk, and it would be dishonest to skip it. If many clients live on the same machines, you have to be certain one client can never see, touch, or accidentally get mixed up with another. "We'll just be careful" is not good enough — careful is exactly how leaks happen.
So we built firm walls between tenants on purpose. Each client's sign-ups are kept in their own locked drawer, not tossed into one shared pile. Every contact is stamped with which client it belongs to, so one client's mailing list can never be sent to as if it were another's. Those walls are enforced by the system itself, not by anyone remembering to be tidy.
Building those walls properly the first time is the part that took real work. But once they exist, the next client is almost free. We added one of our own ministry's projects as the next tenant on the very same setup. That was an afternoon's building, not a fresh shopping trip.
The first client pays to set up the shared workshop. Every client after that just brings their own wood.
One honest warning
This is not an argument for cramming everything onto one machine to save a few rand. Sharing concentrates your risk as surely as it concentrates your savings. We run several public websites through one shared connection, and we learned the hard way that one careless restart can knock all of them offline at once. When everyone shares, everyone shares the bad days too.
So the trade is deliberate. You accept that concentrated risk in exchange for near-zero cost on each new client, and you pay for it with discipline: firm walls between tenants, real caution around anything that touches a shared service, and a backup you have actually tested by restoring from it — not one you merely hope works.
The lesson is narrower than "host everything yourself." It is this: when a client needs something built, look first at what you already run. If your shared setup has proper walls between clients, the first client pays to stand it up and every client after lands almost for free. A sign-up form for one conference becomes the foundation the next client sits on. That was the whole point of building it this way.
Under the hood
The "infrastructure we already run" was a concrete, public stack — nothing in this build was a new line item.
- Front end: a serverless page on Cloudflare Pages, where we already host static sites. The form's server-side logic ran as a small Pages Function (Cloudflare's lightweight serverless handler).
- Storage: registrations write into a self-hosted Postgres-backed database we already operate.
- Marketing automation: new contacts sync into a self-hosted Mautic instance (open-source marketing automation) for the follow-up sequence, pushed through the Pages Function.
- Bot protection: Cloudflare Turnstile — free, and already present in the Cloudflare account.
- Backup: a nightly dump to object storage we already pay for.
The tenant boundary is what makes shared infrastructure safe rather than reckless:
- Registration data lives in its own Postgres schema with row-level security (RLS) — not mixed into a shared table.
- Every marketing contact carries an explicit tenant tag, so one client's audience can never be addressed as another's.
- The Pages project is its own deploy target with its own secrets.
Because that boundary was designed in from the start, adding the firm's own ministry as the next tenant on the same database and the same Mautic instance was a build, not a procurement cycle — a schema, a Pages project, and a handful of functions.
The blast-radius warning is real and lived. The self-hosted database served four public hostnames through a single shared tunnel. A careless restart of that one shared service took all four hostnames down at once — concentrated failure is the price of concentrated cost savings. The mitigations that keep it sane: schema isolation, real care around restarting anything that fronts multiple tenants, and a backup you have actually restored from rather than merely configured.