ADR-001: Use SQLite Instead of PostgreSQL
- Status: Accepted
- Date: 2026-02-22
- Authors: Bruno Paz
Context
Net Worth Tracker is a single-user personal finance application intended for self-hosted, local deployment. The primary use case is an individual tracking their own net worth over time — not a multi-tenant SaaS with concurrent users or distributed infrastructure.
A database engine must be chosen to persist accounts, snapshots, and application state. The two most common options considered were SQLite and PostgreSQL.
Decision
Use SQLite (via better-sqlite3) as the database engine, managed by Drizzle ORM.
Rationale
SQLite fits the deployment model
The application is designed to run as a single Docker container on a personal machine or homelab. SQLite stores the entire database in a single file, which means:
- No separate database process to manage or monitor
- No network socket, authentication, or connection pooling required
- Backup is a simple file copy (
cp networth.db networth.db.bak) - The Docker image remains self-contained — no need for a
compose.yamlwith a PostgreSQL service
Concurrency is not a concern
SQLite serialises writes, which would be a limitation for high-concurrency workloads. This app has a single user and predictably low write frequency (a few snapshots per month). WAL mode (enabled by default in better-sqlite3) handles the read/write overlap without issue.
Operational simplicity wins at this scale
Running PostgreSQL for a personal app introduces unnecessary operational overhead: separate container lifecycle, health checks, init scripts, volume management, connection strings with credentials, and migration bootstrapping. SQLite eliminates all of that.
Drizzle ORM abstracts the dialect
Using Drizzle ORM means the query layer is not deeply tied to SQLite specifics. If requirements ever change (e.g., multi-user support, hosted deployment), migrating to a PostgreSQL-compatible Drizzle dialect is achievable with targeted schema and config changes — not a full rewrite.
Consequences
Positive
- Zero-dependency deployment: one container, one file
- Simple backup and restore story
- Fast local performance with synchronous
better-sqlite3API - No credential management for the database layer
Negative
- Not suitable if the app evolves into a multi-user or remotely-hosted service
- SQLite dialect has minor feature gaps vs PostgreSQL (e.g., no
RETURNINGwithUPDATEin older versions, limitedALTER TABLEsupport) - Horizontal scaling (multiple app replicas sharing one DB) is not possible without switching databases
Alternatives Considered
PostgreSQL
Would be the right choice if the app needed multi-user support, concurrent writes from multiple processes, or was deployed as a hosted service. Rejected for this use case because it adds infrastructure complexity without benefit at personal scale.