Back to Blog
Engineering

How Per-Customer Container Isolation Works

Billy Team·February 24, 2026·8 min read

One Container Per Customer

Most SaaS products run all customers on the same server. Your data sits in the same database, behind the same API, on the same machine as everyone else's. That's fine for a note-taking app. It's not fine when you're syncing an entire Stripe account.

Agent Billy takes a different approach: every customer gets their own isolated Azure Container App with its own PostgreSQL database. Your Stripe data never shares compute, memory, or storage with another customer.

The Architecture

Here's what happens when you sign up:

  1. Provisioning. Our control plane spins up a new Azure Container App in your dedicated resource scope. This takes about 90 seconds.
  2. Database. A fresh PostgreSQL instance is provisioned and connected to the Stripe Sync Engine. Your data lives here and nowhere else.
  3. Sync. The sync engine pulls your Stripe data into the local database. Initial sync takes 30 seconds to 5 minutes depending on your data volume.
  4. Ready. Your dashboard connects to your container, your database, your data.
┌─────────────────────────────────────────┐
│           Azure Container Apps          │
│                                         │
│  ┌─────────────┐   ┌─────────────┐     │
│  │ Customer A  │   │ Customer B  │     │
│  │ ┌─────────┐ │   │ ┌─────────┐ │     │
│  │ │  Billy  │ │   │ │  Billy  │ │     │
│  │ │  API    │ │   │ │  API    │ │     │
│  │ └────┬────┘ │   │ └────┬────┘ │     │
│  │ ┌────┴────┐ │   │ ┌────┴────┐ │     │
│  │ │ Postgres│ │   │ │ Postgres│ │     │
│  │ └─────────┘ │   │ └─────────┘ │     │
│  └─────────────┘   └─────────────┘     │
└─────────────────────────────────────────┘

Why Not Multi-Tenant?

Three reasons:

1. Security Boundaries

In a multi-tenant database, a SQL injection or ORM bug could expose one customer's data to another. With per-customer isolation, a vulnerability in one container affects exactly one customer — and it's the customer whose data is already in that container.

2. Performance Isolation

Stripe sync can be resource-intensive. A customer with 500,000 invoices shouldn't slow down dashboard loads for a customer with 500. Separate containers mean separate CPU, memory, and I/O budgets.

3. Data Residency

Some customers need their billing data in a specific Azure region. Per-customer containers make this trivial — we just provision in the requested region.

The Cost Question

"Doesn't this cost more?" Yes, slightly. Azure Container Apps charges based on resource consumption, so idle containers cost very little. The per-customer overhead is roughly $3-5/month in infrastructure costs, which is well within our pricing margins.

We think the security and performance guarantees are worth the tradeoff. Our customers' Stripe data is their most sensitive business asset, and treating it like commodity multi-tenant data doesn't feel right.

What This Means for You

  • Your data is physically isolated. Not just logically separated by a customer_id column — actually running on separate compute.
  • You can delete everything. When you cancel, we destroy the container and database. There's no orphaned data in a shared table somewhere.
  • Performance is predictable. Your dashboard speed depends on your data volume, not your neighbors' data volume.