B2B SaaS platform · 2024–2025
From one cluster per customer to one platform
A SaaS product was scaling infrastructure cost linearly with customers. I designed the tenancy model that broke that curve — real isolation, per-tenant cost visibility, and an escape hatch for the customers who genuinely needed one.
- Clusters
- 40 → 6
- Onboarding time
- 3 days → 12 min
- Infra cost per customer
- −64%
The situation
One dedicated Kubernetes cluster per customer. It had been the right decision at ten customers — simple isolation story, easy to reason about, easy to sell. At forty it had become the main constraint on the business.
Onboarding a customer took three days of a platform engineer's time, so sales could not promise a start date without checking the engineering calendar first. Infrastructure cost scaled linearly with revenue rather than sub-linearly, which meant every new customer arrived with a fixed cost attached and gross margin got slightly worse as the company grew. And a two-person platform team was hand-maintaining forty near-identical clusters that had quietly drifted apart — the kind of drift where nobody can tell you which clusters have the fix and which have the bug, because the answer is in forty places.
The company had noticed the cost curve. What they had not yet framed was that the three-day onboarding was the same problem wearing different clothes.
The constraint
- Enterprise customers had contractual isolation language that a naive "everyone shares a namespace" model would violate
- Two customers were EU public-sector bodies with data-residency terms that named a specific region and prohibited processing outside it
- Migration had to be per-customer and reversible; a big-bang cutover was unacceptable to the account teams
- The platform team had no capacity to operate something more complex than what they had
That last constraint mattered most and is the one usually ignored. A tenancy model the team cannot run is worse than the problem it solves.
What I did
Designed a shared multi-tenant platform with isolation that was enforced rather than assumed:
- Namespace per tenant with hierarchical RBAC, so tenant-scoped access could be delegated without granting cluster-wide rights
- Network policy isolation as the default posture — deny cross-tenant traffic, allow by exception
- Resource quotas and priority classes per tenant, so one customer's batch job cannot degrade another's interactive traffic
- Admission policy enforcing the boundaries at the API server, so isolation is a property of the cluster rather than a convention in a runbook
- Per-tenant cost showback via OpenCost, which gave the commercial team unit economics per customer for the first time
- A documented escape hatch — a dedicated-cluster tier for the small number of customers with genuine hard-isolation requirements, priced accordingly
That last point was the one that made the design acceptable to sales. Pretending one model fits every customer is how these projects stall.
The outcome
- Cluster count reduced from 40 to 6 — four shared regional clusters plus two dedicated ones for the customers who genuinely needed them
- Customer onboarding moved from three days to about twelve minutes, fully automated. Sales stopped checking the engineering calendar.
- Per-customer infrastructure overhead down 64%, and — more usefully — no longer linear. The next hundred customers cost meaningfully less each than the last forty did.
- Platform team reclaimed roughly fifteen hours a week previously spent on cluster maintenance, which they spent on the developer platform instead
- Unit economics per customer visible to the commercial team for the first time. Within a month this had changed how two contracts were priced — which was not a goal of the project and was arguably its highest-value outcome.
What I'd do differently
I'd have built the cost showback first, before any of the tenancy work. It was sequenced last because it looked like reporting — the nice-to-have you add once the real engineering is done. In fact it was the thing that made the whole project defensible: the moment the commercial team could see cost per customer, the migration stopped being an infrastructure project competing with the roadmap and became something the CFO asked about in Monday meetings. Two weeks of work that would have bought political cover for the following three months.
I'd have written the escape-hatch tier before designing the shared model, not after. I built the shared platform, then defined the dedicated-cluster option when sales raised the two public-sector accounts. Doing it in that order meant retrofitting the provisioning path to handle both shapes, and about a fortnight of avoidable rework. Deciding up front which customers will never share infrastructure is a five-person conversation, and it constrains the design in ways that are cheap at the start and expensive in month three.
I'd resist the pull to migrate the easy customers first. We did, because it builds confidence and produces early wins. But the easy customers taught us nothing, and the first genuinely awkward tenant — a customer with an unusual ingress arrangement nobody had documented — surfaced a design gap at customer twenty-nine rather than customer three. Migrating one difficult tenant early would have been slower and would have found that gap when changing the design was still cheap.