Early access for Channel Partners and ISVs opening soon. Learn more →

Engineering

FastYoke DB: why we ship two flavors of distributed SQL

FastYoke Engineering · 8 min read · Apr 25, 2026

  • Databases
  • Architecture
  • SQLite
  • Distributed SQL

We start every tenant with a file

FastYoke's default posture is unusual, and we like it that way: every tenant gets its own SQLite database file. Not a shared schema with a tenant_id column bolted on — an actual, separate file on disk, one per tenant.

That choice buys us three things we didn't want to give up. Isolation, because one tenant's queries physically cannot touch another tenant's rows — there's no shared table to leak across. Locality, because the data your workflows read and write lives next to the process running them, so a lookup is a syscall, not a network hop. And an ejection clause that's a single download: if a customer wants to leave, we hand them a file. There's no export job, no "give us two weeks," no proprietary dump format to reverse-engineer. It's just their database.

For the overwhelming majority of what FastYoke runs — single-region deployments, one operator's job queue, one warehouse's inventory — a file per tenant is not a compromise. It's the right architecture, plainly, and we built the whole platform around it.

When one VM stops being enough

But some tenants outgrow it, and they outgrow it for reasons that have nothing to do with data volume. A single SQLite file lives on a single VM. That's fine until a customer needs high availability across regions, or a disaster-recovery posture that survives losing a data center, or genuinely concurrent writers in two geographies at once. A file on one machine can't do any of that, no matter how fast the machine is.

This is a well-known shape of problem, and we didn't have to invent the framing for it. The idea of decomposing a large multi-tenant system into independently-scalable, blast-radius-limited units — a "cell" per customer or per shard, rather than one monolithic shared backend — is the same instinct behind cell-based architecture: keep the isolation boundary at the tenant, and let each cell scale (or fail) on its own. Our per-tenant SQLite file already gives us that boundary at small scale. The question was how to keep the same boundary once a tenant needs more than one VM can offer.

Why not just move everyone to one shared database

The tempting shortcut is to solve this once, for everyone: stand up a single large distributed database, migrate every tenant onto it, and retire the per-tenant file entirely. We considered it and rejected it, because it throws away the exact properties that make FastYoke's default posture worth having.

A shared cluster means a shared blast radius — a noisy neighbor, a bad migration, or a runaway query now has a path to every tenant's data, not just its own. It means locality disappears — every read is a network hop to wherever the cluster happens to be, for tenants who never needed that latency in the first place. And it means the ejection clause stops being a file download and starts being a support ticket: extracting one tenant's slice out of a shared distributed schema is real engineering work, every time, for every customer who wants to leave.

Most tenants never need HA, geo-distribution, or multi-region writes. Forcing all of them onto infrastructure sized for the few that do is a bad trade — worse isolation and worse economics, for a scaling property almost nobody asked for.

The choice: keep the default, add a second substrate

So we didn't replace per-tenant SQLite. We designed FastYoke DB alongside it: a managed distributed-SQL layer a tenant can move onto when its workload actually needs that shape, while every other tenant stays exactly where it already was. The intent is a managed service in the literal sense — FastYoke provisioning and operating the distributed cluster on your behalf on a managed distributed-SQL engine, so you don't manage the cluster, you choose the engine. This is the direction FastYoke DB is built toward; the per-tenant file remains the default every tenant runs on today.

The part we care about most is that this is additive, not a fork of the platform. The same application code, the same migrations directory, the same FSM guards, the same append-only event log are meant to run unchanged regardless of which substrate is underneath — handlers shouldn't carry an if/else for "which database am I on." Moving a tenant from a file to a managed cluster changes the engine underneath it. It should not change a single line of the code that tenant's workflows run on.

That's the property we're building toward: the database tier can change shape without the application tier noticing. A tenant on FastYoke DB and a tenant on plain SQLite are meant to be, from the handler's point of view, running the identical platform.

Why two engine flavors, not one

Once we'd decided to offer a managed distributed-SQL option, the next question was which engine. We didn't want to pick just one — FastYoke DB is designed around two, YugabyteDB and CockroachDB, so that a tenant on FastYoke DB would choose which one sits underneath.

That's a deliberate refusal to make a single-vendor bet on our customers' behalf. The two engines are excellent for genuinely different reasons, and the right one depends on the workload and the procurement constraints, not on which one we happened to integrate first:

  • YugabyteDB ships as Apache 2.0 open source, speaks the Postgres wire protocol, and carries mature support for LISTEN/NOTIFY, SKIP LOCKED, and JSONB — the kind of Postgres-native behavior a lot of application code already assumes. It's the engine we expect to point most distributed deployments at by default, including customers who need a fully open-source posture for compliance reasons.
  • CockroachDB is built around geo-distribution and consistent global writes, with a strong change-feed story for reacting to data as it moves across regions. It's licensed under BSL beyond its free tier, which is a real procurement input, not a footnote — but for buyers whose latency requirements are genuinely global, it's worth that tradeoff.

Neither engine is the "upgrade path" from the other. They solve overlapping but distinct problems, and a customer who picks Yugabyte for its open-source licensing today isn't locked out of Cockroach's geo story later — the FastYoke DB layer is designed to speak to both through the same portability contract, so the choice is about workload and procurement fit, not about which integration happens to exist.

What that buys the customer

Step back, and offering both engines behind one portability layer pays off in three concrete ways.

No single-vendor lock-in. A customer chooses an engine based on licensing posture and geo requirements today, and isn't stuck with that decision if the calculus changes — the application code, schema, and migrations are the same regardless of which engine sits underneath.

Operational fit over one-size-fits-all. HA, DR, and multi-region writes aren't one workload shape — a fintech customer's compliance posture and a logistics customer's geo-latency requirement can point at different engines, and we'd rather let the workload pick than force a single answer.

Continuity. The line we keep coming back to: you don't outgrow FastYoke by outgrowing a single VM. The per-tenant file is still the default and still the right choice for most tenants. For the ones that need more, the database underneath changes — the platform they've already built on does not.

Where this fits with the rest of the platform

This is the same instinct that shaped two other decisions we've written about: why FastYoke's core engine is Rust memory-safe and predictable at the process layer, and why we chose WebAssembly to sandbox tenant logic — isolation and portability as defaults, not retrofits. FastYoke DB extends that same posture down into the storage layer: isolate by default, and give customers a real choice when they need to scale past it.

If you're evaluating whether your workload needs HA, geo-distribution, or DR guarantees beyond a single VM, take a look at FastYoke DB — including which engine flavor fits your compliance and latency constraints.