SQLite as Embedded Database
Desktop applications require a database for persistent storage.
Context
Desktop applications require a database for persistent storage. The choice of database technology significantly impacts deployment complexity, data portability, user privacy, and application architecture. For local-first desktop applications, users should have full control of their data without requiring external database servers. The application should be distributable as a single package without complex setup, and users should not need to install, configure, or manage database servers. Traditional client-server databases like PostgreSQL, MySQL, or SQL Server require separate installation, network connectivity, user authentication, and ongoing maintenance, adding unnecessary complexity to a desktop application.
Decision
SQLite is the embedded database for desktop applications. It is an embedded, serverless database engine that stores data in a single file on the user's local filesystem.
- Embedded: SQLite runs in-process with the application, requiring no separate server.
- Serverless: No configuration, no setup, no administration.
- Self-contained: The database is a single cross-platform file.
- Zero-configuration: Works out-of-the-box with no installation required.
- Full SQL support: ACID-compliant with comprehensive SQL features.
- Cross-platform: Works identically across macOS, Linux, and Windows.
- Use WAL (Write-Ahead Logging) mode for improved concurrent access:
PRAGMA journal_mode=WAL. - Set
PRAGMA busy_timeout = 30000as the first PRAGMA after opening the database to handle file locks from cloud sync services. - Enable foreign key constraints:
PRAGMA foreign_keys=ON. - Store database files in the user's application data directory, not the installation directory.
Do's and Don'ts
Do
- Store SQLite database files in the user's application data directory.
- Implement proper file-based backup strategies (the entire database is a single file).
- Use WAL mode for improved concurrent access.
- Set
PRAGMA busy_timeoutas the first PRAGMA after opening the database. - Use
PRAGMA foreign_keys=ONto enforce referential integrity. - Handle database file permissions appropriately for user privacy.
- Close database connections properly to release file locks.
Don't
- Attempt to use SQLite for high-concurrency multi-user scenarios.
- Store the database file in the application installation directory.
- Assume cloud-synced directories work reliably with SQLite WAL mode (use DELETE journal mode for cloud paths).
- Run any PRAGMA before
busy_timeout(if the first PRAGMA hits a lock, it fails instantly). - Use connection pooling (SQLite uses a single file, connection overhead is minimal).
- Bypass SQLite's type affinity system.
Consequences
Positive
- Zero setup required; users start immediately without installing database servers.
- True data ownership in a single portable file.
- Privacy by default; data never leaves the user's device unless explicitly exported.
- Simplified distribution as a standalone binary.
- Easy backups by copying a single file.
- No network latency; all operations are local.
Negative
- Limited concurrency; only one writer at a time.
- Not suitable for multi-user concurrent access patterns.
- Performance and reliability depend on the underlying file system.
- No built-in replication; synchronization between devices requires custom implementation.