Postgres
SQLite is the zero configuration default, running in WAL mode with a single serialized writer. It comfortably handles small and mid-size teams. The schema is deliberately portable, so moving is a migration rather than a rewrite.
When to move
Section titled “When to move”Move when you have a concrete reason: several application nodes, a managed backup story you already trust, or a team large enough that write contention shows up in practice. Do not move because Postgres feels more serious. A single SQLite writer will outrun most teams’ actual message rate.
Moving
Section titled “Moving”node dist/index.js migrate-to-postgres postgres://user:pass@host:5432/slickThis copies every table into a freshly migrated, empty Postgres database and
prints per-table row counts for verification. It preserves workspace_events
sequence values, which is the part that matters: connected clients resume where
they left off instead of being forced into a full resync.
Then restart with DATABASE_URL set. The files directory is used as is.
Differences
Section titled “Differences”Both dialects use TEXT ULID identifiers and epoch-millisecond integer timestamps, so application behaviour is identical. Two things differ under the hood:
- Full text search uses a generated
tsvectorcolumn with a GIN index instead of SQLite’s FTS5. - Epoch timestamps are
biginton Postgres, since they overflowinteger.
backup remains SQLite only. On Postgres, use pg_dump.