Back to blog
engineering

SQLite vs Postgres: Which Warpkit Template Fits Your Product

By Daniel Chavez

SQLite vs Postgres: Which Template Fits Your Product

Both templates ship in the same $199 purchase. This isn't "which do I buy," it's "which do I clone." The decision is architecture, not pricing , you already own both.

The Core Tradeoff

Per-user SQLite gives you isolation by construction: every user gets their own database file, so cross-user data leaks are architecturally impossible, not just policy you have to remember to write correctly. We've made that case in detail before.

The Postgres fork trades that guarantee away on purpose. Shared Postgres means every table has a user_id column and every query filters on it , isolation by policy, not by construction. That's not a downgrade. It's the price of being able to query across users at all, which per-user SQLite can't do without a separate aggregation step.

Neither template is the "real" one. They're two answers to two different questions.

Real Technical Tradeoffs

Query latency. SQLite reads happen on the same machine as your app, no network involved , the data is a local file. Shared Postgres almost always lives on a different machine, so every query is a real network round trip: send the request, wait, get the answer back. One query per request is fine. Where this actually bites: a page that loops over 20 items and runs a separate query for each one pays that round trip 20 times instead of once , a mistake per-user SQLite apps never have to think about, because a local file read is fast enough that nobody notices the same mistake there.

Connection overhead. SQLite doesn't "connect" to anything , it just opens a file, which costs nothing. Postgres is a separate server you talk to over the network, and opening a new connection to it isn't free , doing it on every request would slow everything down and can exhaust the database's limit on how many connections it'll accept at once. So Postgres deployments keep a pool of already-open connections ready to reuse (on Cloudflare Workers, Hyperdrive does this for you; elsewhere it's PgBouncer or a host's built-in pooler like Neon's or Supabase's). That's a real piece of infrastructure to configure, and a real ceiling on how much traffic you can handle at once , not a one-time setup step you forget about.

Concurrency model. SQLite allows one writer at a time, per file. When the file only belongs to one user, that's not a limitation , nobody else is ever trying to write to it at the same time. It's a real problem for anything shared: two teammates editing the same document, a counter multiple people are incrementing at once. Postgres allows many writers to the same table at the same time, which is what shared data like that actually needs.

Cold starts vs. idle cost. "Cold start" is the delay a serverless platform pays the first time it spins up a fresh instance to handle a request , after that, the same instance stays warm and handles more requests instantly, until it's been idle long enough to get shut down again. Deploy the Postgres fork to Cloudflare Workers and you pay no cost while nobody's using it, but the first request after an idle period pays that startup delay, plus the time to open a fresh database connection. Self-host either template and there's no cold start , the process is always running , but you're paying for that server every hour, whether anyone's using it or not. Neither option is free; they're just different bills.

Cross-user queries. In per-user SQLite, "show me activity across all users" has no direct answer , there's no single database containing everyone's data, just N separate files, one per user. Getting that answer means opening every file yourself and combining the results in your own code. In Postgres, since everyone's data is already in the same tables, it's one query. If your product needs that kind of cross-user view even occasionally, per-user SQLite is working against you, not for you.

When SQLite Wins

  • CPU-intensive, per-user workloads. A heavy per-user computation , generating a report, indexing a document, running a batch import , runs against that one user's own file. It's not sharing a database connection or competing for resources with every other user's traffic happening at the same time, the way it would on one shared database serving everyone at once.
  • Read-heavy, personal-data apps. Dashboards, journals, trackers, personal knowledge bases , local file reads beat a network round trip every time. If the product is mostly "load my stuff and look at it," SQLite is the faster path by default, no extra tuning required.
  • Any B2C product where user data is genuinely independent. Personal productivity tools (notes, habit trackers, personal CRMs), per-user AI assistants and chat history, solo analytics and finance dashboards, internal admin tools where each operator's workspace is separate, API/dev-tool SaaS scoped to one account, content tools where a user's drafts never need to be queried alongside anyone else's. If two users' data never needs to appear in the same query, you get real isolation for zero extra code , see the full use-case list from the first post for more.

When Postgres Wins

  • Team/B2B products with shared workspaces. If multiple people write into the same dataset , a shared project board, a team's shared documents, a workspace multiple teammates edit , per-user SQLite doesn't fit by definition, there's no "the team's file," only individual users' files. This is the fork's actual reason to exist.
  • Social, marketplace, or any feature where one user's action needs to show up in other people's views. Feeds, follows, comments, cross-listing search, matching buyers to sellers. Example: when someone posts, that post needs to appear in every one of their followers' feeds , one action, many people who need to see it. To build "show me what everyone I follow posted," Postgres does it in one query. Per-user SQLite can't: each user's posts live in a separate file, so there's no single query that spans them , the app has to open every followed user's file one at a time and stitch the results together itself.
  • Admin dashboards that need numbers across every user. "Total signups this week," "top 10 accounts by usage," anything you as the operator need to see across your whole user base , one query in shared Postgres. In per-user SQLite, the same answer means opening every user's file and adding up the numbers yourself.
  • You specifically need a Cloudflare Workers deploy. Per-user SQLite needs a persistent disk between requests; Workers doesn't give you one. If serverless is a hard requirement, not a nice-to-have, the fork is built for exactly that.

Not a Strict Upgrade

Don't read the Postgres fork as "warpkit 2.0." It's warpkit's own patterns , auth, billing, error handling, background jobs , rebuilt on a Postgres/serverless stack for the buyer who specifically needs that tradeoff. The per-user SQLite template isn't being deprecated or de-prioritized; it's still the better architecture for the product shape it was built for.

Pick based on your product's actual data shape, not on which one sounds more "modern." Both are yours either way , see the Postgres docs or the pricing FAQ for the rest.