SQLite in Production: When It's the Right Choice (And When It Isn't)


SQLite has an image problem in web development. It’s seen as the database you use for learning SQL or for local testing before deploying to Postgres. “Not a real database” is the common dismissal, usually from developers who’ve never seriously evaluated it for production use.

That reputation is wildly out of sync with reality. SQLite is the most widely deployed database in the world. It’s in every Android phone, every iPhone, every major web browser, and countless embedded systems. It’s also increasingly being used as the primary database for web applications where it makes architectural sense.

Let me walk through when SQLite is the right choice for production, when it isn’t, and what’s changed in the last few years to make it more viable for web apps.

What SQLite Actually Is

SQLite is a C library that implements a self-contained, serverless, zero-configuration SQL database engine. “Serverless” here means there’s no separate database server process—the database is a file on disk that your application reads and writes directly.

This is fundamentally different from Postgres, MySQL, or other client-server databases where your application connects over a network to a separate database server process. SQLite runs in the same process as your application.

The advantages of this architecture:

  • No network latency: Reading from SQLite is reading from a local file. For small queries, this can be 10-100x faster than querying a remote database.
  • Simple deployment: The “database server” is just a file. Copy the file, and you’ve copied the entire database.
  • Zero configuration: No users to set up, no ports to configure, no connection pooling to tune.
  • Atomic, consistent, durable: SQLite provides full ACID compliance. Transactions are rock-solid.

The disadvantages:

  • Single-writer concurrency: Only one process can write to the database at a time. Multiple readers are fine, but concurrent writes require serialisation.
  • No network access: You can’t query the database from a remote client. It’s local-only unless you build your own network layer.
  • Limited to a single machine: Horizontal scaling requires architecture changes.

When SQLite Is the Right Choice

Single-server applications with moderate write loads. If your application runs on one server (or one primary server with read replicas), SQLite can handle thousands of writes per second. The SQLite documentation suggests SQLite works well for applications handling up to 100,000 requests per day. In practice, I’ve seen it handle much more.

Edge-deployed applications. If you’re using edge compute platforms like Cloudflare Workers or custom AI development deployments where latency to a centralised database is a bottleneck, SQLite on the edge node eliminates round-trip time.

Content-heavy sites with infrequent writes. Blogs, documentation sites, and marketing sites read far more than they write. SQLite’s read performance is exceptional, and the simplicity of deployment (just copy a file) makes it attractive for static-ish sites.

Embedded applications. If you’re building desktop software, mobile apps, or IoT devices, SQLite is often the default choice. It’s small (under 1MB compiled), fast, and reliable.

Development and testing. Even if you use Postgres in production, using SQLite in development can simplify onboarding (no database server to install) and speed up tests (in-memory SQLite databases are incredibly fast).

When SQLite Isn’t the Right Choice

High-concurrency write workloads. If you have hundreds of clients all trying to write to the database simultaneously, SQLite’s single-writer limitation becomes a bottleneck. A client-server database with row-level locking (Postgres) handles this much better.

Multi-server deployments without replication strategy. If your application runs across multiple servers and they all need write access to the same database, SQLite doesn’t work without additional architecture (like LiteFS or rqlite for replication).

Very large datasets. SQLite handles databases up to 281 terabytes in theory, but practical performance degrades as database size grows beyond a few gigabytes without careful tuning. For truly massive datasets, distributed databases make more sense.

Complex analytical queries. SQLite’s query optimiser is good but not as sophisticated as Postgres’s. For complex joins, aggregations, and analytical workloads, Postgres or a specialised analytical database performs better.

What’s Changed: SQLite for Web Apps Is Now Viable

A few developments in the last couple of years have made SQLite more attractive for web applications:

Litestream: A tool that continuously streams SQLite changes to object storage (S3, R2, etc.). This provides disaster recovery and enables point-in-time restore. Before Litestream, backing up SQLite in production was awkward. Now it’s straightforward.

LiteFS: A distributed file system from Fly.io that replicates SQLite databases across multiple nodes with strong consistency. This addresses SQLite’s single-server limitation by providing transparent replication.

Turso: A managed SQLite hosting service that provides edge-deployed replicas with write forwarding. You get the benefits of SQLite’s read performance everywhere while writes go to a primary node.

Improved tooling: The ecosystem around SQLite—ORMs, migration tools, and client libraries—has matured significantly. Prisma, Drizzle, and other modern ORMs support SQLite with the same features as Postgres.

These tools eliminate most of the operational friction that previously made SQLite impractical for web apps.

Real-World Examples

Fly.io’s internal applications: Fly uses SQLite with LiteFS for several internal tools. Their blog has detailed write-ups on the architecture.

Expensify: The expense tracking app famously used SQLite for years as their primary database, serving millions of users. They’ve since migrated to a more complex architecture, but SQLite served them well during high growth.

Git: Version control metadata is stored in SQLite. Every git repo has an SQLite database in .git/objects.

Notion (originally): Notion started with SQLite before scaling to Postgres. For early-stage startups, SQLite’s simplicity is a huge advantage.

Practical Recommendations

If you’re building a new application and considering SQLite, here’s my advice:

Start with SQLite if: You’re deploying to a single server or edge compute, you expect moderate traffic, and you want simple deployment. Add Litestream for backups. Migrate to Postgres if and when you actually hit SQLite’s limits, not preemptively.

Use Postgres if: You need multi-server write concurrency from day one, you’re building a complex multi-tenant application, or you’re certain you’ll need advanced Postgres features (full-text search, PostGIS, etc.).

Don’t let “SQLite isn’t a real database” influence your decision. It’s a real database. It’s just optimised for different use cases than Postgres. Choose based on your actual requirements, not perceived credibility.

SQLite’s biggest advantage is simplicity. One file. No server. No configuration. For many applications, that’s exactly what you need.