# One Organisation, One Region: Running a Multi-Tenant Finance Product Across Isolated Regions

**Subtitle:** Why I shipped a product as four isolated regions instead of one app with replicas, how login finds the right region while signup doesn't, and how every region was held to the same strict standard.

---

*Reading Time: ~6 minutes • Target Audience: Backend Engineers, Platform Engineers, anyone who has been asked "where does our data live?" by a customer*

---

## 1. The decision

Farben is a multi-tenant finance product. At some point customers (and their auditors) ask a simple question: *which country is our data in?*

There are two ways to answer it.

1. **One app, many replicas.** Keep one logical system and place copies of the data near users. The honest answer becomes "it depends on which replica, which backup, which cache, which log line".
2. **Isolated regions.** Run the whole stack once per region. Each copy is complete and independent. The answer becomes "it is in that region, and that region cannot reach the others' data".

I chose the second, and the rule that falls out of it fits in one sentence:

> **One organisation, one region. Login finds the region. Signup does not.**

## 2. The shape

Production runs in four isolated regions on GCP: the UK, the EU, India and Singapore. The UK is the original environment. The others follow the same blueprint.

```
                    browser
                       |
        +--------+-----+-----+--------+
        |        |           |        |
       UK       EU          IN       SG      <- one entry point per region
        |        |           |        |
    compute  compute     compute  compute
    database database    database database   <- no shared app data,
    storage  storage     storage  storage       no replication
    keys     keys        keys     keys
    AI       AI          AI       AI
```

Every region has its own compute, database, object storage, encryption keys and model endpoints. An organisation exists in exactly one region.

## 3. What each region guarantees

**A private database.** Managed Postgres reachable only over a private network, with no public endpoint. Connections must be encrypted, access is audited, and point-in-time recovery is on.

**Regional storage.** Files live in object storage in the same region as the organisation.

**Regional keys.** Encryption keys are managed in the same region as the data they protect. They are per tenant and created the first time an organisation needs one, so encryption is isolated per customer as well as per region.

**Regional AI.** Chat and embeddings call models hosted in the same region. There is deliberately **no cross-region fallback**. If the regional model is unavailable, the request fails. I prefer a visible failure to a silent trip across a border.

**TLS per host.** Each regional entry point has its own address and its own certificate. A wildcard doesn't help: it says a certificate may be used for a name, not where that name resolves, and I need different names to land in different regions.

## 4. Enforcement, not convention

A residency policy that exists only in a wiki will be broken by the first tired engineer on the first bad day. So the service itself refuses. If a region is asked to store data for an organisation that belongs elsewhere, it rejects the request. The check lives in the code, not in a deploy checklist.

Isolation is structural too: each region has its own database on a private network, so there is no connection that could reach another region's data by mistake.

## 5. Signup doesn't choose a region. Login finds one.

I decided early that **country must not choose the region**. People travel, companies have offices in several places, and a billing address is a poor proxy for a data-processing agreement.

**Signup** writes the organisation into whichever regional site the browser opened. No geo-IP guess, no hidden rewrite of your tenancy.

**Login** is where the lookup happens:

1. The region you opened checks whether it owns the account.
2. If not, it does a single lookup in a small **routing directory** that maps an email to its region. It does not call the other regions one by one.
3. The browser continues on the owning region.
4. The password is entered once, on the owning region, and goes nowhere else.

The principle is **least data in the shared piece**. The directory holds only what is needed to answer "which region owns this mailbox?", and nothing else. No application data and no credentials live in it.

## 6. A strict standard in every region

Each region has its own residency and data-handling expectations, and I treated them as hard constraints rather than best effort. The same bar applied everywhere:

- **Enforced in code,** so a violation is a rejected request instead of a policy review.
- **Encrypted** in transit, over private networking, and per tenant with regional keys.
- **Audited,** with database access logged and point-in-time recovery available.
- **Identical across regions,** so no region is the "weaker" one. The new regions were built from the same blueprint and held to the standard the original set.

Compliance stays boring on purpose. If every region is built the same way, auditing one is auditing all of them.

## 7. Tradeoffs I accepted

- **One small shared piece.** The routing directory is the one thing every region depends on, so it has to be tiny, highly available and free of application data. In exchange, login makes one lookup instead of a fan-out: no stacked per-region latency and no partial failures when one region is down.
- **Several of everything.** More infrastructure to run. Keeping the regions identical means operating one teaches you the rest.
- **No cross-region reporting by default.** With no shared application database there is no single place for a global query. Anything global has to be assembled per region.
- **Moving an organisation isn't a toggle.** With no replication, changing region is a deliberate export-and-import exercise.

In return: a residency answer I can state in one sentence per category, a blast radius that stops at the region boundary, and failures that are loud instead of quietly crossing a border.

## 8. Takeaways

1. **Pick the unit of isolation first.** "One organisation, one region" made later decisions (routing, storage, keys, models) mechanical.
2. **Enforce residency in code.** A rejected request matters more than a policy document.
3. **Don't let proxies like country decide tenancy.** Signup writes where you opened it; login finds where you live.
4. **Keep the shared piece minimal.** One lookup table that maps email to region, with no application data and no credentials.
5. **Prefer failing to silently falling back.**
6. **Build every region the same.** One standard, applied everywhere, is what makes compliance auditable.

---

*I'm Shivam, a software engineer working on backend infrastructure and hybrid-cloud systems. More at [shivamshahi.com](https://www.shivamshahi.com).*
