Kubernetes

One EKS control plane, many tenants, several regions

Eight months building a multi-tenant EKS platform where each jurisdiction is isolated by namespace, network policy and IRSA rather than by its own cluster, with ArgoCD ApplicationSets deploying every tenant.

Confidential, election management8 months5 engineersUpdated August 18, 2026
70%
less overhead than cluster-per-tenant
99.99%
uptime
GitOps
for every deploy

An election management platform serves jurisdictions that each require their data to stay inside a particular geography and each expect to be unaffected by anyone else's load. The obvious answer is a cluster per jurisdiction. That answer costs a fortune and nobody can patch it.

The decision that shaped everything

One EKS cluster per region, tenants isolated by namespace. This is only defensible if the isolation is enforced by the platform rather than by convention, so three things had to be true before anything else was built:

  • Network policy denies cross-namespace traffic by default, with Calico enforcing it, so a tenant cannot reach another tenant's service even by IP
  • IRSA gives every workload its own IAM role, so no pod inherits node permissions and no pod can read another tenant's S3 prefix
  • OPA Gatekeeper rejects anything that tries to escape: host networking, privileged containers, missing resource limits, workloads without a tenant label

Those three together are what makes namespaces sufficient. Any one of them missing and the argument collapses.

Regions

Regional placement is a legal requirement, not a latency optimisation. A tenant's workloads run in the region its jurisdiction names, and the deployment pipeline is identical across all of them. Route 53 sends each tenant's hostname to its own regional load balancer; there is no cross-region failover for data-resident tenants, because failing over would be the violation.

Deployment

A management cluster runs ArgoCD. ApplicationSets generate one application per tenant per region from a Git repository, so onboarding a jurisdiction is a pull request that adds a directory.

  • Tenant configuration in Git, reviewed like code
  • No kubectl access to production clusters for humans in the normal path
  • Drift is corrected automatically, which means the cluster state and the repository do not diverge quietly

Nodes

Karpenter provisions nodes to fit pending pods rather than to fill a fixed node group, with Spot capacity for the workloads that tolerate interruption. Election workloads are extremely peaky. Quiet for months, then enormous for a week. Fixed capacity sized for that peak is idle most of the year.

Infrastructure code

Terraform with Terragrunt for the region and cluster layer. Terragrunt is doing real work here: the same module gets applied to every region with a small per-region input file, so a change lands everywhere or nowhere rather than in whichever region someone remembered.

Where it landed

  • About 70% less operational overhead than the cluster-per-tenant design it replaced, measured in clusters to patch and control planes to pay for
  • 99.99% uptime across the platform
  • No cross-tenant data access in any test or audit to date
  • Every deployment through GitOps, with no manual production changes

What was hard

Gatekeeper policies. Written too loose they prove nothing, written too tight they block legitimate work and teams route around them. Getting to a set that engineers accept took several iterations and a lot of conversations about specific rejected manifests.

What we still do

Cluster upgrades, policy maintenance, tenant onboarding review, and the on-call rotation across regions.

Project

Client

Confidential, election management

Duration

8 months

Team

5 engineers

Category

Kubernetes

Amazon EKSArgoCDKarpenterTerraformTerragruntCalicoOPA GatekeeperPrometheus
Questions

Questions about this build

Anything missing, email [email protected].

Namespace isolation or a cluster per tenant?

Namespaces, if you are willing to enforce them. Network policy denying cross-namespace traffic by default, IRSA so no workload inherits the node role, and OPA Gatekeeper rejecting anything that tries to escape. A cluster per tenant is simpler to explain and far more expensive to run and patch.

Why does data sovereignty force multi-region?

Some jurisdictions require their data to stay inside a geography. That is a placement rule, not a performance one, so the design puts tenant workloads in the region the law names and keeps one deployment pipeline across all of them.

What does Karpenter change here?

Nodes get provisioned to fit the pods actually pending, per tenant, including Spot where the workload tolerates interruption. Fixed node groups sized for peak sit idle the rest of the month.

How do tenants get deployed?

ArgoCD ApplicationSets on a management cluster generate one application per tenant per region from a Git repository. Adding a tenant is a pull request, not a runbook.

More work

Two others we can talk about

Kubernetes

EKS Cluster Setup for Enterprise Application

Complete Amazon EKS cluster setup with advanced networking, security, and monitoring for a large-scale enterprise application serving millions of users.

Read it
Migration

Moving an insurance ledger to AWS without stopping the business

Nine months moving core policy and transaction services off on-premises virtualisation into a multi-account AWS Organization, with Transit Gateway carrying traffic between 30+ VPCs and the remaining datacentre.

Read it

Send us your AWS bill

Thirty minutes with an engineer, no slide deck. We will tell you what we would change first and what it would cost to have us do it.

Talk to an engineer