SQLite in production is fine. Really.
Your app has 400 users, not 400 million. I did the math so you can relax.
Every few months a client asks me to justify the database, and the conversation goes the same way. They have four hundred users. They have been told, by someone with a diagram, that they need a managed Postgres cluster with a read replica in a second region. What they need is a file.
The Fort Greene Supper Club runs on SQLite. Twenty-four seats, three services a week, a waitlist that gets busy on Thursdays. Peak traffic is about nine requests a second, all of it from people deciding what to eat. The whole database is 34 MB and fits in the page cache with room for the entire menu history since 2023.
The numbers nobody checks
A single write transaction on an ordinary SSD takes well under a millisecond in WAL mode. Reads are a function call — no socket, no TLS handshake, no connection pool waiting to be exhausted at the worst possible moment. If your app does ten writes per request and serves ten requests a second, you are using about ten percent of one core.
PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA busy_timeout = 5000; PRAGMA foreign_keys = ON;
Those four lines are most of the configuration story. WAL lets readers and a writer work at the same time. The busy timeout turns the one error everyone hits — SQLITE_BUSY — into a short wait instead of a stack trace. Set them at connection time and get on with your afternoon.
The question isn’t whether SQLite scales. It’s whether you will ever be big enough to find out.
When I do reach for Postgres
- More than one machine genuinely needs to write. This is the real line, and it is the only one I take seriously.
- You want a query planner that handles analytical joins across millions of rows without hand-holding.
- Your team already runs Postgres well, and the marginal cost of one more database is zero.
Notice that none of those are “we might get big”. You can migrate later, and later you will know things about your data that you cannot know now. Halstead Books moved from SQLite to Postgres last spring, after two years, in an afternoon, because the schema had been shaped by two years of real orders.
Back up with the native backup API or litestream to object storage, not by copying the file while a write is in flight. That is the one way to lose data here, and it is entirely avoidable.
— O.P., FORT GREENE, 34 MB AND HAPPY