SQLite vs Postgres for Indie Apps: How to Choose
Choosing between SQLite and Postgres sounds like a simple question until you realize the answer changes completely depending on whether you have one server or ten, one user or ten thousand. This post gives you a concrete framework for SQLite vs Postgres for indie apps — not a theoretical comparison, but a decision you can make before you write a single migration file. By the end you will know which database fits your deployment model, your traffic shape, and your tolerance for operational overhead.
The real difference is not features — it is the deployment model
Both SQLite and Postgres are mature, reliable relational databases. Both support transactions, foreign keys, and complex queries. The feature gap that matters to most indie projects is small. The operational gap is enormous.
SQLite is a file. It lives on the same machine as your application process. There is no server to start, no connection pool to tune, no authentication layer to configure. Deployment is copying a file. Backup is copying a file. The entire database engine ships inside your application binary.
Postgres is a server. It runs as a separate process, listens on a port, manages its own memory, and requires a connection string. That separation unlocks horizontal scaling, concurrent writes from multiple application instances, and a rich ecosystem of managed hosting options — but it also means you are operating a second piece of infrastructure from day one.
For an indie builder running a single-server app with modest traffic, that operational gap is the whole story.
When SQLite is the right call
SQLite earns its place when your deployment is a single process on a single machine. That covers more production apps than most developers admit.
Read-heavy tools and internal dashboards. SQLite handles thousands of reads per second without complaint. If your app is mostly reading — a personal finance tracker, a link archiver, a blog with a small admin panel — SQLite will never become your bottleneck.
Apps where simplicity is a feature. Every dependency you eliminate is a failure mode you eliminate. No Postgres container means no container to crash, no credentials to rotate, no connection pool to exhaust. Indie apps frequently die from operational complexity, not from database limitations.
Edge and embedded deployments. Platforms like Fly.io with persistent volumes, Cloudflare D1, and Turso (which replicates SQLite to the edge) have made SQLite a first-class production citizen. If you are deploying to a single region with a persistent disk, the infrastructure story is now genuinely excellent.
The hard limit to respect: SQLite serializes writes. One write at a time, full stop. WAL mode softens this — readers no longer block writers — but concurrent write-heavy workloads will hit a wall. If your app accepts form submissions from hundreds of simultaneous users or processes background jobs that write in parallel, SQLite will queue those writes and latency will climb.
When Postgres is the right call
Postgres earns its place the moment your deployment model breaks the "single process, single machine" assumption.
Multiple application instances. If you run two web dynos, two containers, or two replicas for availability, they cannot share a SQLite file safely. Postgres is designed for exactly this — multiple clients, concurrent writes, serializable isolation.
Write-heavy workloads. Job queues, event ingestion, collaborative editing, anything where many users write simultaneously — Postgres handles concurrent writes without serializing them at the application level.
You expect to hire. Every developer you will ever hire knows Postgres. The tooling, the mental models, the debugging workflows are universal. SQLite in production still surprises people, and surprised engineers make mistakes.
You need managed hosting. Neon, Supabase, Railway, and Render all offer Postgres with automated backups, point-in-time recovery, and connection pooling. The operational burden that made Postgres intimidating on a raw VPS is largely abstracted away by 2026. If you are already paying for a managed platform, the ops argument for SQLite weakens considerably.
Side-by-side decision table
| Factor | SQLite | Postgres |
|---|---|---|
| Deployment complexity | Minimal — file on disk | Moderate — separate server or managed service |
| Concurrent writes | Serialized (WAL helps reads) | Full concurrent writes |
| Multiple app instances | Not safe without replication | Native support |
| Managed hosting options | D1, Turso, Fly volumes | Neon, Supabase, Railway, Render, and many more |
| Backup strategy | Copy the file | Managed snapshots or pg_dump |
| Local dev experience | Zero setup | Requires Docker or local install |
| Hiring / team familiarity | Uncommon in production | Universal |
| Cost at small scale | Effectively free | Free tier on most managed platforms |
| Best fit | Single-server indie apps, tools, dashboards | Multi-instance apps, write-heavy workloads, teams |
The migration question nobody asks early enough
A pattern that bites indie builders: they start with SQLite for simplicity, hit a write concurrency wall at traction, and then face a migration under pressure. SQLite and Postgres are compatible at the SQL level for most common operations, but the migration is not zero-effort. Column types differ subtly, AUTOINCREMENT behavior differs, and any raw SQL that leaked SQLite-specific syntax will need auditing.
If you have any realistic expectation of needing multiple app instances within twelve months — because you are building something you intend to sell, or because high availability matters to your customers — start with Postgres. The managed hosting cost at small scale is negligible, and you avoid a painful migration at the worst possible time.
If you are building a tool for yourself, a small internal team, or a side project you genuinely expect to stay small, SQLite is not a compromise. It is the correct choice.
You can explore how different databases map to common project types at /browse, or run your project details through /recommend to get a full stack suggestion that accounts for your deployment model.
For a broader look at how database choice fits into the rest of your stack decision, the /compare tool lets you put SQLite and Postgres side by side against your specific constraints.
If you are also evaluating the tooling around your database — ORMs, migration libraries, admin UIs — matchmytool is worth a look for finding the right supporting tools without wading through every option manually.
Key takeaways
- SQLite is a file; Postgres is a server. That operational difference matters more than any feature comparison for most indie projects.
- SQLite is a legitimate production choice for single-server, read-heavy, or low-write-concurrency apps — not a toy or a compromise.
- Postgres is the right default the moment you need multiple app instances, high concurrent writes, or a team that can maintain the system without tribal knowledge.
- Managed Postgres hosting (Neon, Supabase, Railway) has largely eliminated the ops burden argument against Postgres at small scale.
- If you might need Postgres in twelve months, start there. The migration cost under pressure is higher than the hosting cost today.
- WAL mode closes the gap for SQLite reads but does not fix concurrent writes — know which workload you actually have.
The fastest way to pressure-test your database choice against the rest of your stack is to run your project through /recommend — it surfaces tradeoffs specific to your team size, traffic expectations, and deployment target, not just generic best practices.