EARLY ACCESSConvex-compatible · runs on Bunny

Build like Convex. Own the backend.

BurrowDB is a reactive backend in a single Bun process. You write queries, mutations and actions in TypeScript, and every screen that depends on the data gets fresh results over WebSocket. It runs in a Bunny Magic Container, keeps the data in Bunny Database, and speaks the Convex API.

  • Same API as Convex
  • No SQL to write
  • In production since November 2026
messages.tsChat.tsx
// burrow/messages.ts
export const list = query({
  args: { channel: v.string() },
  handler: (ctx, { channel }) =>
    ctx.db.query("messages")
      .withIndex("by_channel", (q) => q.eq("channel", channel))
      .collect(),
});

export const send = mutation({
  args: { channel: v.string(), body: v.string() },
  handler: (ctx, args) => ctx.db.insert("messages", args),
});

// src/Chat.tsx: live, no refetch
const messages = useQuery(api.messages.list, { channel });
≈ 4 msto commit a mutation to Bunny Database, measured server side
1,105functions of a real Convex app loaded unchanged, auth included
88 / 88pages of a production site rebuilt identical after the move
0lines of SQL in your code
How it works

One process owns the data. Everything else gets simpler.

The hard parts of a reactive backend become plain code when a single server holds the data. That is the trade: one node, one region, and far fewer moving parts.

01 · TRANSACTIONS

Serializable by construction

Mutations run one at a time inside a SQLite transaction. No conflict detection, no retry loops, no write skew to reason about.

02 · REACTIVITY

Push only what changed

While a query runs, BurrowDB records the index ranges it read. After a commit it re-runs only the live queries a write touched, and every result it pushes carries the same version, so a screen never mixes old and new.

03 · DURABILITY

Written before it counts

Each mutation reaches Bunny Database in one atomic batch before its promise resolves. If that fails, nothing changes. The local copy is rebuilt at startup, so there is no volume to manage.

The code

TypeScript in, live data out.

A project keeps its backend in one folder. Every exported function becomes an endpoint, typed end to end.

  • Files are modules: messages.ts exports list, the client calls api.messages.list.
  • Arguments and documents are validated with v.*; ids are checked against the database.
  • Indexes live in the schema, and every scan uses its index: no table scans by accident.
  • A resolved mutation means your live queries already show it.
// burrow/messages.ts
export const recent = query({
  args: { channel: v.string() },
  handler: (ctx, { channel }) =>
    ctx.db
      .query("messages")
      .withIndex("by_channel", (q) => q.eq("channel", channel))
      .order("desc")
      .take(50),
});

export const send = mutation({
  args: { channel: v.string(), body: v.string() },
  handler: async (ctx, args) => {
    const id = await ctx.db.insert("messages", { ...args, author: "me" });
    // written in the same transaction: exists only if send commits
    await ctx.scheduler.runAfter(0, internal.notify.push, { id });
  },
});
Port from Convex

Bring your Convex app. Keep your code.

The public API mirrors Convex: query, ctx.db.query().withIndex(), v.*, _generated. Moving a project is an import, a flag and a deploy.

1

Import your data

Export from Convex, then load the snapshot. Documents keep their ids, and stored files come along.

$ npx convex export --path snap.zip \
    --include-file-storage
$ burrow import snap.zip
2

Keep your imports

Flag the app "convex": true and your functions keep importing convex/*. The Vite plugin does the same for the front end.

// host.json
"shop": { "project": "../shop",
  "dir": "convex", "port": 3301,
  "convex": true }
3

Bundle and deploy

One image for one app or several. Run it on Magic Containers and point your domain at it.

$ burrow bundle host.json --push
# then: Magic Containers → your image

What carries over

  • Queries, mutations, actions, internal functions
  • Scheduler and crons
  • HTTP actions and httpRouter
  • JWT auth and @convex-dev/auth
  • .paginate and usePaginatedQuery
  • ctx.storage and signed file URLs
  • Nested runQuery and runMutation
  • HTTP client for SSR and scripts
  • Full-text searchnext
  • Componentsnext
In production
e-croatie.com moved from Convex to BurrowDB with its backend unchanged apart from the imports.
28 / 28
REST and MCP routes answer the same
88 / 88
site pages rebuilt identical
1 container
shared with another app
Production details

The boring parts, already handled.

Daily backups

A consistent snapshot every day to a private storage zone, kept 30 days. burrow restore brings one back.

Several apps, one container

One port per app, one domain per port. Commits are compare-and-set, so moving an app to another container never loses a write.

Health checks

/_burrow/health for the platform; add ?check to probe the database, the storage zones and the age of the last backup.

Private file storage

Bytes in a private Bunny storage zone, served through signed links. Metadata lives in the database like any document.

Auth that already works

OpenID Connect and custom JWT providers. @convex-dev/auth runs as is: sign-up, sign-in, token refresh.

Jobs you can trust

Scheduled mutations run exactly once, scheduled actions at most once. Crons in UTC, jobs survive restarts.

HTTP routes

Webhooks and REST endpoints with httpRouter. Bearer tokens reach ctx.auth like everywhere else.

Reconnects on its own

After a restart, clients reconnect and re-subscribe without a line of code on your side.

Honest limits

What BurrowDB is not, yet.

One process is a choice. Here is what it costs today, so you can decide before you move.

One node, one region

If the server stops, requests wait until it restarts. Clients reconnect on their own. Put the container and the database next to your users.

A ceiling on writes

Each commit is a round trip to Bunny Database: roughly 150 to 200 writes per second per app today. Grouped writes are next on the list.

Bunny Database is in beta

Databases are capped in size during the beta (1 GB today). BurrowDB runs its own daily backups on top.

Next
  • Grouped writes
  • Full-text search
  • Dashboard
  • Components
  • Public npm release under @burrowdb
FAQ

Questions developers ask first.

Is BurrowDB affiliated with Convex?

No. BurrowDB is an independent project that implements the same public API, so Convex projects can move without a rewrite. Convex is a trademark of its owner.

Do I have to use Bunny?

In production, the data lives in Bunny Database. The server itself is a Bun process in a container; Magic Containers is where we run and test it. In development, everything sits in a local SQLite file.

How fast is it?

With the container and the database in the same region, a mutation commits in about 4 ms on the server. Reads come from the local SQLite copy. Measured from Slovenia against a production deployment, HTTP actions answered in 33 ms at the median.

What happens to my existing data?

burrow import reads a Convex export: every document keeps its id, so references between tables still hold, and stored files are imported with their metadata.

What if the server goes down?

Nothing committed is lost: a mutation only resolves once Bunny Database has it. While the process restarts, clients wait, then reconnect and re-subscribe. The local copy is rebuilt from the database at startup.

Can I try it today?

BurrowDB is in early access. Tell us about your project and we will help you move it.

Keep the way you build. Own where it runs.

Tell us what you run on Convex today. We will tell you what moves as is, what needs work, and help you through the first deploy.