Monzo: a bank built on thousands of microservices
At a glance
Section titled “At a glance”| Organization | Monzo, a UK retail bank |
| Period | 2016 to 2023 |
| Problem | A bank that must be available around the clock and scale to many markets and teams |
| Architecture | Microservices from the start, on Kubernetes (opens in a new tab), with Cassandra (opens in a new tab), Kafka (opens in a new tab) and heavy standardization |
| Patterns | Microservices, Kubernetes, platform engineering |
The problem
Section titled “The problem”Traditional banks often rely on nightly batch processing, single points of failure and maintenance windows. Monzo wanted none of those: its systems had to be available all day, every day, and scale to many customers in several markets with many independent teams. In 2016 its head of engineering explained why it chose a distributed microservices backend from the start, unusual for a three-person backend team.
What they did
Section titled “What they did”2016: the foundations. By then Monzo ran about 150 services, mostly written in Go:
- Containers on Kubernetes, after a year on Mesos and Marathon. Monzo said moving roughly cut its production infrastructure cost to a quarter.
- HTTP between services, with Linkerd (opens in a new tab) on each host handling load balancing, routing and retries of idempotent requests.
- Kafka for asynchronous messages, chosen for availability, persistence, replay and at-least-once delivery.
- Shared logic exposed as services over RPC rather than copied as libraries.
2023: scale through standardization. At QCon London 2023 Monzo described roughly 2,000 or more services, over 300 engineers, 7 million customers and hundreds of deployments a day, including Fridays. What keeps that manageable is a paved road:
- Every service starts from the same template. Deviations must be deliberate.
- Shared library layers handle HTTP, metrics, serialization and database access.
- Service interfaces are defined in Protobuf and code is generated from them.
- A pipeline generates Kubernetes manifests, so engineers rarely write YAML.
- Each new service automatically gets dashboards, alerts and an owner in a service catalog.
- Cassandra is the main database. Each service has its own keyspace, and reading another service’s keyspace is forbidden.
- Static analysis blocks patterns the platform team has retired.
flowchart TB
tmpl[Service template + shared libraries] --> svc[New service]
proto[Protobuf interface] -- code generation --> svc
svc --> k8s[Kubernetes: generated manifests]
svc --> ks[(Own Cassandra keyspace)]
svc --> kafka{{Kafka}}
svc --> obs[Auto dashboards, alerts, owner]
Results
Section titled “Results”Monzo grew from a three-person backend team to a regulated bank serving millions of customers, deploying hundreds of times a day.
Trade-offs
Section titled “Trade-offs”- A large platform investment. Templates, libraries, code generation and tooling are maintained by dedicated platform teams.
- Many network hops. A single customer action can touch many services, so tracing and good defaults for timeouts and retries are essential.
- Strict rules. Teams give up some freedom of choice in exchange for consistency.
Lessons you can reuse
Section titled “Lessons you can reuse”- If you run many services, make the standard path the easiest path: templates, generated code and automatic observability.
- Enforce data ownership technically, for example with a separate keyspace or schema per service.
- Decide early how services talk synchronously and asynchronously, and give every team the same tools for both.
Related on ArchitectureCompass
Section titled “Related on ArchitectureCompass”Sources
Section titled “Sources”- Oliver Beattie, “Building a Modern Bank Backend” (opens in a new tab), Monzo, September 2016
- Suhail Patel, “Banking on Thousands of Microservices” (opens in a new tab), QCon London 2023, published by InfoQ
Written and edited by Abhinav Taduka, enterprise software architect.