Practice & Reference · Case Study · Real company

Monzo: a Bank Built Cloud-Native from Day One

Most banks run core ledgers that predate Kubernetes by decades, bolting "digital" onto a mainframe underneath. Monzo, a UK challenger bank launched in 2015, is the unusual case of a regulated financial institution that chose microservices, containers, and a cluster orchestrator before it had a single customer — and has published an unusually large amount of detail about what that actually took. This case sticks to what Monzo's own engineers have said in public: their engineering blog, KubeCon talks, CNCF write-ups, and on-the-record interviews. Where the public record thins out — exact current headcounts, a full org chart, precisely how every trade-off was resolved — this page says so rather than filling the gap.

☺ Explain it like I'm 10

Imagine building a giant train station from scratch, and instead of one huge building that does everything, you build thousands of small, identical booths — one for tickets, one for lost property, one for the timetable board — each booth built from the same blueprint so any worker can walk into any booth and know how it works. That's Monzo's bank: thousands of small services instead of one giant program, all stamped from the same mould, with security guards checking that booth A is only allowed to talk to the three booths it actually needs, not every booth in the station.

🦊🐢Your hosts for this case file: Foxy & Timmy the Turtle — Foxy investigates how a five-year-old bank ended up running thousands of microservices without it turning into chaos, and Timmy explains the guardrails — zero-trust networking, policy-as-code, multi-party approval — that a regulator-scrutinised platform needs bolted in from the start, not added later.

The starting situation: cloud-native in an industry that usually isn't

☺ Like you're 10: Most banks' computers are old and slow to change, like a building that's been added onto for fifty years. Monzo poured a brand-new foundation instead.

A microservices bet before the beta launch

Monzo's own 2016 engineering-blog post, "Building a Modern Bank Backend," is candid that this was a deliberate, early bet, not a retrofit. Starting with three backend engineers, the team chose a distributed microservices architecture over a monolith from the very first line of code, and by the time of their public beta had already grown to "nearly 100 services." The reasoning the post gives is direct: isolating each piece of banking logic — card payments, ledger entries, notifications — into its own small, independently deployable service meant a bug or a slow deploy in one area couldn't take down the whole bank, and different teams could ship on their own schedule without stepping on each other. For a five-person startup building a regulated financial product, that is an unusually infrastructure-heavy first move — most startups that size are still arguing about which framework to use for a single app.

Mesos, then Kubernetes — and a real cost number

The same 2016 post describes an early switch in cluster orchestration: Monzo started on Mesos and Marathon, then moved to Kubernetes running on CoreOS while Kubernetes was still young technology (Monzo's later CNCF write-up dates their adoption to Kubernetes 1.2, in 2016). The stated payoff is a specific, attributable number rather than a vague "it's better": Monzo's own post states that after the move to Kubernetes, "our production infrastructure costs about a quarter of what it did before" — a roughly 75% infrastructure cost reduction, which The Register's 2020 coverage of Monzo separately cited in the 65–70% range depending on the comparison window. Either figure is a real, sourced number; treat the exact percentage as directional rather than a single audited constant, since it was reported at different points in Monzo's growth.

🦊 Foxy's read

"Notice what's not in this story: no rip-and-replace of a legacy core banking system, because there was no legacy system yet. Monzo's advantage wasn't cleverness — it was starting empty. That's the biggest single caveat on this whole case file, and it's worth remembering every time you read a section below that sounds enviably clean."

Architecture decisions: Go, a polyglot RPC layer, and the "paved road"

☺ Like you're 10: They picked one main building material (a programming language called Go) for speed and simplicity, but let a few special rooms use other materials when it made sense — then made sure every booth followed the same blueprint.

Go as the default, polyglot at the edges

Monzo's 2016 post describes choosing Go as their primary language for "low-latency, high-concurrency servers," while deliberately keeping the door open to Java, Python, and Scala where a specific team or workload benefited — a pragmatic polyglot stance rather than a mandate. For the RPC layer connecting services, they picked plain HTTP specifically because every language can speak it, then layered Linkerd in front of services as an out-of-process proxy to handle retries, load balancing, and connection pooling, so individual services didn't need to reimplement that logic themselves. For asynchronous work they adopted Kafka, valued for a replicated, partitioned design that scales horizontally without service interruption. For storage, Monzo settled on Apache Cassandra as its primary transactional datastore — a decision this case study returns to below, because it is also where Monzo's most publicly documented outage happened.

The "paved road": one shape for every microservice

A 2021 write-up on Monzo's platform, drawing on comments from staff engineer Suhail Patel and summarised on the Container Solutions engineering blog, describes what Monzo calls a "paved road": a defined, pre-generated starting point for any new microservice, wired into the same ORM layer, the same queueing hooks, and the same observability defaults, so that "the microservices all look very uniform." The stated goal is that a new service should look and feel like every other service a Monzo engineer has ever touched — which is what makes it realistic for ~200 engineers (a figure reported by The Register in 2020) to collectively operate a fleet that had already passed 2,000 microservices by the time of Monzo's 2022 security blog post, and reportedly exceeds 3,000 today per Monzo's own platform-engineering writing on migrating to EKS. The uniformity is also a security control in its own right: because every service is built the same way, a security patch or a runtime upgrade can roll out once, everywhere, instead of needing thousands of bespoke fixes.

◆ Key idea

Monzo's paved road is the same underlying idea this course teaches on Platform as a Product & Golden Paths, applied at the level of "what does a brand-new microservice look like on day one," not just "how do I deploy." Suhail Patel is quoted describing the platform's goal bluntly: an engineer should be able to have "their entire Monzo career... and never have to interact with Kubernetes" directly — the deploy tool (Shipper, an in-house tool that abstracts Kubernetes complexity and enforces policy) is the interface, not kubectl.

Backstage, adopted — not built in-house

Notably, Monzo did not build its own version of a service catalog. The same platform write-up describes Monzo adopting Backstage, Spotify's open-sourced developer portal (see the Spotify case study for its origin), around September 2020 — using it to hold service ownership, dependency, and business-classification metadata, and extending it with custom plugins for deployment history and internal "software excellence" scoring. That's a useful data point against the Spotify case: a large platform team with the resources to build its own tools still chose to buy (adopt, really — Backstage is free and open source) rather than build a catalog from scratch, because the catalog itself is not where Monzo's differentiated engineering value lives.

From self-hosted Kubernetes to EKS — the hard way, then the easier way

☺ Like you're 10: They first built and ran their own robot factory by hand for years. Once cloud companies started selling a much better pre-built factory, they moved everything over — one booth at a time, checking each one still worked.

Why they self-hosted, and what it cost them

In a KubeCon + CloudNativeCon Europe 2020 talk titled "Banking on Kubernetes, the Hard Way, in Production," Monzo engineers Miles Bryant and Suhail Patel explained that Monzo self-hosted Kubernetes largely out of necessity: when they adopted it in 2016, managed offerings like GKE and EKS were either immature or didn't yet exist in a form Monzo could trust for a regulated bank. That meant operating, upgrading, and customising their own control plane, networking, and cloud integrations by hand for years — including, by the speakers' own account, an incident where a change to network policies took down a large portion of the cluster. Summarising the same talk, the CNCF published coverage under the headline "Monzo: we learned a lot from self-hosting Kubernetes, but we wouldn't do it again" — a title Monzo's own speakers are reported to have agreed with, on the reasoning that the operational toil of running Kubernetes itself had stopped paying for itself once managed alternatives matured; as Patel put it, that work "doesn't bring you much leverage nowadays."

The migration: a purpose-built migrator service, not a big-bang cutover

Monzo's own platform-engineering writing describes the eventual move off self-hosted Kubernetes onto Amazon EKS as a gradual, automated migration rather than a single risky cutover. True to the "build a service, don't run a manual runbook" philosophy that shows up across Monzo's platform posts, the team built a dedicated backend migrator service that could be handed a list of microservices at the start of a day, migrate them one by one with health checks at each stage, and roll a migration back automatically if a service looked unhealthy after moving. By Monzo's own account, that migrator moved more than 3,000 microservices onto the new EKS cluster.

⚠ What the public record doesn't give you

Monzo has not published an exact EKS cutover date, a full incident count during the migration, or a precise final head-to-head cost comparison between the self-hosted era and EKS. Treat "self-hosting for years, then migrating thousands of services with an automated migrator" as the accurate shape of the story — not a story with every date and dollar figure filled in.

Zero-trust networking and policy-as-code security

☺ Like you're 10: Every new booth starts with zero friends. It has to be introduced to exactly the booths it's allowed to talk to — nothing more — and a robot checks the introduction list on every change.

rpcmap: discovering dependencies by reading code, not watching traffic

An InfoQ report on Monzo's network-isolation work describes a hard scaling problem: with roughly 1,500 microservices in production at the time and over 9,300 documented inter-service interactions, manually maintaining a network policy per service pair was never going to work. Monzo's answer was a custom tool called rpcmap, which discovers real service dependencies through static analysis of the codebase itself — reading which services call which RPC methods in source — rather than by observing live traffic, specifically because banking systems have rare, seasonal, or edge-case code paths ("there isn't an integration test for everything") that live traffic sampling could miss. Running on every commit via Calico's Kubernetes NetworkPolicy integration, rpcmap converts those discovered dependencies automatically into Kubernetes labels and policy rules, and Monzo shifted ownership of each rule from the destination service to the calling service, grouping especially chatty services by a shared "service-type" label to keep the rule count from exploding. At the time of that InfoQ report, Monzo had a documented intention to eventually move this enforcement from the CNI layer onto a service mesh built on Envoy.

Zero trust as a written platform principle, not just a tool

Monzo's March 2022 engineering-blog post, "How we secure Monzo's banking platform," generalises this into an explicit zero-trust principle: a brand-new microservice is born with no default access to any other service, database, or the public internet — every permission is opt-in and reviewed, and internet egress is allowlisted by DNS name at a per-microservice level. The same post describes several concrete controls worth naming individually, since each maps directly onto a concept covered on this course's own Security & Policy Enforcement page:

Control (Monzo's own description)What it doesThe course concept it maps to
Kubernetes NetworkPolicies (via rpcmap/Calico)Deny-by-default service-to-service traffic, opt-in per dependencyZero trust & NetworkPolicy
Policy-as-code & multi-party authorisation (MPA)Sensitive infrastructure changes and operations require more than one approving human, enforced by tooling, not just a review-culture normPolicy engines & admission control
Concourse CI pipelines to staging, then productionAll infrastructure changes are peer-reviewed and roll out through an automated pipeline — no hand-edited production configGitOps-adjacent change control
HashiCorp VaultSecrets are issued to each microservice via templated policies scoped to that service specificallySecrets management
TeleportAn auditing SSH proxy with full session recording and playback for any human access to infrastructureAudit trails
Hardware security keys for MFAEnforced, phishing-resistant multi-factor authentication for staff, preventing credential sharingIdentity & access controls
STRIDE threat modelling with OWASP Threat DragonA structured framework for reasoning about attacker classes on new systems before they shipDesign-time security review

The same post also describes comprehensive audit logging — AWS CloudTrail, Kubernetes audit events, and network traffic logs feeding centralised systems for anomaly detection — and a "break glass" emergency-access mechanism that grants infrastructure access during genuine incidents while triggering loud alerts and still requiring multi-party authorisation for the most critical actions. That combination — deny by default, but with an audited, alarmed emergency door — is the practical shape of "zero trust" for a regulated environment: not "nobody can ever get in during an emergency," but "getting in during an emergency is loud, logged, and requires more than one person."

⚠ Watch out — this is a bank, not a typical SaaS platform

Several of these controls (MPA on sensitive operations, mandatory threat modelling, annual access reviews across every SaaS tool) exist because Monzo operates under UK financial regulation and PCI DSS obligations for card data, not because "more security is always better" in the abstract. A smaller, unregulated platform team copying every control here wholesale will slow itself down for compliance requirements it doesn't actually have. Match the control to the regulatory weight you're actually carrying.

Observability, autoscaling, and on-call at thousands-of-services scale

☺ Like you're 10: With thousands of booths, you need a control room that can see every booth's dials at once, robots that top up staffing when a booth gets busy, and a fair rota so no one person is always the one woken up at 3am.

Prometheus, Alertmanager, Grafana, and Thanos

Monzo's 2018 engineering-blog post, "How we monitor Monzo," describes migrating their monitoring stack, over roughly six months, onto Prometheus for metrics collection, Alertmanager for routing alerts to Slack and PagerDuty by team ownership and severity, Grafana for dashboards, and the Thanos project layered on top for long-term, multi-server queryable storage backed by S3. Alert rules and runbooks live in Git, following a pull-request-review-then-automatic-deploy flow — the same GitOps-adjacent discipline this course teaches in GitOps Workflows, applied to monitoring configuration rather than application manifests. All components except the Thanos compactor scale horizontally, a deliberate choice given that this stack has to keep working precisely when the rest of the platform is under the most stress.

Autoscaling: three modes, and a real war story about the fourth

A 2020 Monzo engineering-blog post, "Autoscaling Monzo," describes offering services three explicit scaling modes — manual, vertical (via the Kubernetes Vertical Pod Autoscaler), and horizontal (via the Horizontal Pod Autoscaler) — so a team can pick the model that fits their service rather than one size fitting all. Services typically start conservatively, requesting "no more than 10 millicores each" across three replicas. The post is unusually candid about a real incident during rollout: an early VPA configuration issue caused about 3,500 pods to be evicted and rescheduled within 25 minutes, overwhelming the Kubernetes API server. The documented fixes were concrete: rate-limiting evictions, adding Pod Disruption Budgets so VPA couldn't evict too many replicas of the same service at once, and switching HPA/VPA to read container-specific metrics after discovering the two autoscalers were fighting each other over sidecar-proxy resource usage. Monzo's own summary is that the rollout ultimately shipped with "zero negative impact on the operation of the bank" — a claim about the eventual outcome, not a claim that the rollout was smooth throughout.

On-call and incident response: Response, PagerDuty, and a technical incident manager

Monzo has published a multi-year trail of posts on how its on-call process evolved: "How we do on-call at Monzo" (2018) on PagerDuty-based scheduling and alert severity; a later post on introducing shadow on-callers — engineers new to on-call who observe real incidents before graduating onto the primary rota; and "How we respond to incidents" (2019), which describes Response, an open-sourced, Slack-based ChatOps tool Monzo built and later published on GitHub. Response automatically spins up a dedicated incident channel, prompts the incident lead to set a severity (critical, major, minor, or trivial) within 15 minutes, nudges toward status-page updates, and logs follow-up actions so postmortems aren't reconstructed from memory. A 2022 post, "Scaling our on-call process," describes introducing a technical incident manager role — senior engineers trained specifically in major-incident coordination, separate from whoever is closest to the broken code — as the incident volume that comes with thousands of services outgrew what a single on-call engineer could coordinate alone. Monzo's stated philosophy on customer communication is to "default to transparency," updating their public status page liberally even for incidents affecting only a minority of customers, on the reasoning that honesty compounds trust faster than a spotless-looking dashboard does.

◆ Key idea

Every tool named in this section — Response, the migrator service, Shipper, rpcmap — is the same pattern repeated: Monzo's platform team's default answer to "we keep doing this risky thing by hand" is write a service that does it instead, not write a longer runbook. That instinct — encode the runbook into software rather than into a wiki page — is worth stealing on its own, independent of any specific tool named here.

A documented failure: the July 2019 Cassandra outage

☺ Like you're 10: One day they tried to add six new helpers to their filing-cabinet room, misunderstood one setting, and the new helpers confidently told customers "there's nothing here" about files that were actually just somewhere else.

Monzo published a detailed, named postmortem of a real outage: "We had issues with Monzo on 29th July. Here's what happened, and what we did to fix it." On 29 July 2019, starting at 13:10 BST, customers were unable to reliably log into the app, send or receive payments, withdraw cash from ATMs, or see accurate balances and transactions, for roughly two hours until most service was restored at 15:08, with full recovery completed by 23:00 that day.

The root cause was a scaling operation on their Cassandra cluster: the team added six new Cassandra servers with the auto_bootstrap configuration flag set to false, expecting the new nodes to stay inactive until Monzo manually kicked off the data-streaming process. In reality, that single flag controls two behaviours at once — whether a new node joins the cluster's active ring and whether it streams data automatically — so the new servers immediately began actively serving live queries for data they didn't yet have, and customers saw missing balances and failed transactions as a direct result. Monzo's post commits to three concrete fixes: more rigorous testing of Cassandra operational procedures before running them in production, additional monitoring and alerting for symptoms like "row not found" errors that would have caught the problem sooner, and — the structural fix — splitting their single large Cassandra cluster into multiple smaller ones, so that a future operational mistake has a smaller blast radius instead of taking down the whole datastore at once.

🦆 Dot's-eye view

"What I actually trust here isn't 'they never make mistakes' — obviously they do. It's that the postmortem names the exact flag, the exact misunderstanding, and the exact structural change (splitting one big cluster into several smaller ones) that makes the same class of mistake matter less next time. That's the difference between an apology and an engineering fix."

This is the single most useful worked example on this page for this course's own Reliability, SLOs & Incident Management lesson: a blameless, specific, publicly-shared postmortem that names a root cause precisely enough to prevent a repeat, and pairs the fix with an architectural change (smaller, more isolated clusters) rather than only "we'll be more careful next time."

Organisational choices: squads, dual leadership, and platform as a product

☺ Like you're 10: Small teams of mixed specialists run each area, and instead of one boss doing two different jobs, Monzo split "who plans your work" from "who looks after your career" into two different people.

Small, cross-functional squads

A Monzo engineering-blog post on team organisation, "The way we organise our teams and manage engineers at Monzo," describes small, cross-functional squads of engineers, designers, and product managers, each focused on a specific feature, project, or business area, with teams split further once they outgrow roughly a "two-pizza" size. Each squad owns its services largely independently, which is the organisational precondition for the "paved road" technical pattern above: independent ownership only stays coherent at a scale of thousands of services because every squad's services are built from the same platform-provided template.

Dual leadership: team leads vs engineering managers

The most distinctive detail in that post is a deliberate split of two responsibilities that many companies bundle into a single manager role. A team lead coordinates day-to-day technical work — planning, retrospectives, technical direction for a specific team — while a separate engineering manager is responsible only for an individual engineer's career development, growth, and wellbeing, and is explicitly not tied to whichever team that engineer happens to be staffed on this quarter. Monzo's stated rationale is that this gives each engineer access to what the post calls "twice as many people for support," and lets the company reshuffle squads around shifting business priorities without disrupting an engineer's long-term career relationship with their manager — a direct, organisational answer to the same reorg-thrash problem this course covers on Team Topologies & the Socio-Technical Platform.

Platform engineering as its own product organisation

Monzo's own platform-engineering overview describes the platform team's users as "Monzo engineers" and its product as "the platform" itself — the same platform-as-a-product framing this course teaches on Platform as a Product & Golden Paths. Concretely, that overview describes all of Monzo's backend services living in one shared monorepo and following a common set of patterns, which is what makes it realistic for a relatively small platform team to support thousands of services built by squads it doesn't directly manage: the platform team writes the shared abstractions and dogfoods its own tools, rather than each squad reinventing deployment, observability, and security from scratch.

What changed: the numbers Monzo has actually published

☺ Like you're 10: Fewer, smaller companies could copy this exact playbook, but the ones that could would learn: start with small, uniform pieces, and build a robot for everything you keep doing by hand.

Read across the years, Monzo's own public figures trace a scale trajectory rather than a single before/after comparison: ~100 services at beta (2016 blog), ~150 shortly after (same post), ~1,500 by the time of the network-isolation work described in the 2019 InfoQ report, 1,600 per The Register's March 2020 reporting alongside a team of roughly 200 engineers, 2,000+ by Monzo's own March 2022 security post, and 3,000+ microservices moved by the migrator service onto EKS per Monzo's later platform-engineering writing. The Register also reported engineers shipping to production 70 to 80 times a day as of its 2020 reporting, "and growing fast." Alongside the technology, Monzo has openly published one of the more detailed real-world incident postmortems in the industry (the July 2019 Cassandra outage above) and one of the more detailed real-world security control lists (the March 2022 post) that any bank of its size has put on the public record.

⚠ Read these as directional, not a single audited snapshot

Every number in this section comes from a different blog post or article, published in a different year, describing the platform at a different point in its growth — there is no single Monzo-published table with all of these figures side by side. Treat the trajectory (roughly 100 → 150 → 1,500 → 2,000 → 3,000+ services over about six years) as the honest, sourced shape of the story, not as a precise, internally-consistent dataset you could recompute a growth rate from.

What to steal for your own platform

☺ Like you're 10: Copy the habits, not the headcount — most of what worked here doesn't actually need Monzo's size to work for you.

Honest caveats: what doesn't transfer

☺ Like you're 10: A story that starts with an empty plot of land is a different story from one where you have to rebuild a house you're already living in.

🦊 Foxy's stakeout · 15 min

Pick one microservice at your own company and try to answer, from memory, the same three questions Monzo's platform automatically answers for every service: which other services can it actually reach on the network right now, who owns it, and what does its default dashboard look like without anyone configuring it by hand? If you can't answer instantly, that's the gap between "we have microservices" and "we have a paved road" — and it's exactly the gap Monzo's rpcmap, Backstage, and platform templates each close.

🎬 At the Platform Guild
🦊

Foxy: A five-person startup chose microservices and Kubernetes for a bank. That sounds like over-engineering waiting to happen.

🐢

Timmy: It would be, without the guardrails. But look at what came with it: zero default network access for any new service, policy-as-code, multi-party approval on the sensitive stuff. The complexity bought them room to bolt in serious security from day one, not bolt it on at year ten.

🦆

Dot: I care about one thing — did a new engineer joining Monzo have to learn a different way of building every single service? Sounds like no. One paved road, thousands of times.

👺

Gizmo: Boring! Just let each squad configure Cassandra however they like, why does it need a whole standard? 🤑

🐘

Ellie: Because someone already tried the "trust the flag, skip the second check" shortcut on a live Cassandra cluster, and a two-hour outage is the receipt. I keep that postmortem in my trunk on purpose.

🦫

Benny: And notice they didn't just write a longer runbook after that outage — they split the cluster architecture itself. Same instinct as their migrator service: fix the shape of the system, not just the humans' discipline.

Where this connects in the course

☺ Like you're 10: This one bank's story touches four different lessons — go deeper into whichever matches what you're building right now.

The zero-trust networking, policy-as-code, and audited emergency-access mechanics belong to Security & Policy Enforcement. The Prometheus/Grafana/Thanos stack, golden-signal thinking, and autoscaling story belong to Observability & Operations. The July 2019 outage, blameless postmortem, and on-call evolution are a worked example for Reliability, SLOs & Incident Management. And the squad model, the team-lead/engineering-manager split, and platform-as-product framing belong to Team Topologies & the Socio-Technical Platform. For how Monzo's paved road and Backstage adoption relate to the platform that originated Backstage, see the Spotify case study; for the other real and composite platforms on this evidence board, return to the case studies hub.

🐢 Timmy's checkpoint

1. What specific cost figure did Monzo's own 2016 blog post attribute to moving from Mesos to Kubernetes? 2. Name three concrete security controls from Monzo's 2022 security post, and which zero-trust principle they share. 3. What exact configuration flag caused the July 2019 Cassandra outage, and what was the structural (not just procedural) fix? 4. What did Monzo's KubeCon 2020 speakers say they would do differently about self-hosting Kubernetes? 5. What is the difference between a Monzo team lead and a Monzo engineering manager?

Check your answers
  1. Their 2016 post states production infrastructure cost "about a quarter of what it did before" the move to Kubernetes — roughly a 75% reduction (The Register separately reported a 65–70% figure in 2020 coverage).
  2. Any three of: Kubernetes NetworkPolicies with no default access, policy-as-code with multi-party authorisation, HashiCorp Vault-issued per-service secrets, Teleport-audited SSH access, hardware-key MFA, STRIDE threat modelling. They all share zero trust — nothing is granted by default; every access is explicit and reviewed.
  3. The auto_bootstrap flag was set to false, intended only to delay new nodes joining the active ring — but it also disabled automatic data streaming, so new nodes served queries for data they didn't have. The structural fix was splitting the single large Cassandra cluster into multiple smaller ones, alongside better testing and monitoring.
  4. They said they wouldn't self-host Kubernetes again, now that managed offerings like EKS have matured — the operational toil of running it themselves stopped paying for itself.
  5. A team lead handles day-to-day technical coordination and planning for a specific team; an engineering manager is responsible only for an individual engineer's career growth and wellbeing, independent of whichever team that engineer is currently staffed on.