Skip to content

Monzo: a bank built on thousands of microservices

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

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.

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]

Monzo grew from a three-person backend team to a regulated bank serving millions of customers, deploying hundreds of times a day.

  • 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.
  • 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.

Written and edited by Abhinav Taduka, enterprise software architect.