Skip to content
Crystal Desk

Blog

Engineering

Building Crystal Desk: Why We Went Multi-Tenant from Day One

A short look at some of the architecture decisions behind Crystal Desk — and why row-level tenancy won over schema-per-tenant.

RP

Rajan Pillai, Studio Manager · February 25, 2026 · 4 min read

Every multi-customer SaaS product eventually has to answer the same architecture question: how do you keep one customer's data completely isolated from another's, while still running a single application that's affordable to operate and easy to evolve? There are a handful of standard answers, and we want to walk through why we picked row-level tenancy over the more commonly recommended schema-per-tenant approach, early, before it would have been expensive to change our minds.

The options on the table

Broadly, there are three common patterns for multi-tenant data isolation: a fully separate database per customer, a separate schema per customer within a shared database, or a shared schema with a tenant identifier column on every row, enforced at the query layer. Each trades off isolation strength against operational complexity in a different place.

Database-per-tenant gives the strongest isolation but the worst operational story at scale — migrations have to run N times, connection pooling gets complicated fast, and cross-tenant analytics (which you will eventually want, even just for your own product metrics) become a federated query problem. Schema-per-tenant is the popular middle ground: strong logical isolation, one database instance, but migrations still have to run once per schema, and the number of schemas becomes a real operational variable you have to manage as customers grow into the hundreds.

Why row-level tenancy won for us

We went with a shared schema, tenant_id on every tenant-scoped table, enforced through row-level security policies at the database layer rather than trusted to application code alone. A few reasons this made sense for what Crystal Desk actually is:

Migrations run once. A single schema change — adding a column, adding an index — applies to every tenant simultaneously, with no migration fan-out. For a product still iterating quickly on its workflow engine, this matters more than it sounds like it should. Schema-per-tenant migrations at 50 tenants are annoying; at 500, they become their own engineering project.

Cross-tenant operations stay simple. Product analytics, usage-based billing calculations, and platform-level admin tooling all need to query across tenants sometimes. With row-level tenancy, that's a normal SQL query with a different WHERE clause. With schema-per-tenant, it's a fan-out across N schemas, aggregated in application code.

Enforcement lives at the database, not just the application. Relying purely on "every query in the application code remembers to filter by tenant_id" is a real risk — one missed filter in one endpoint is a data leak. Postgres row-level security policies mean the database itself refuses to return rows outside the current tenant context, regardless of whether a particular application code path remembered to ask correctly. That's a much stronger guarantee than code discipline alone.

What we gave up

The honest tradeoff: row-level tenancy makes true infrastructure-level isolation — a customer wanting their own dedicated database for compliance reasons — a bigger lift later, since it means peeling a tenant's rows out of a shared structure rather than just handing over an existing schema. This is exactly why Enterprise plans support on-premise and dedicated deployment options: for the customers who genuinely need that level of isolation, we offer it as a distinct deployment model rather than trying to force our default architecture to serve every isolation requirement at once.

For the vast majority of teams — a media agency, a production house, an HR team — row-level tenancy has been the right call: simpler operations, faster iteration, and a strong enforcement guarantee that doesn't depend on every engineer remembering every filter, every time.

If you want to see how this shows up for Enterprise customers specifically, the pricing page covers the dedicated and on-premise deployment options available on that tier.

Share:
RP

Rajan Pillai

Studio Manager

Part of the Crystal Desk team.

Like what you read? See it in action.

Start a free trial and build the workflow this post was about — no credit card required.

✓ 14-day free trial   ✓ No credit card required   ✓ Cancel anytime