Skip to main content
Status: ACCEPTED
Date:📅 2026-02-22
Decision Makers:joestump

ADR-0023: Add PostgreSQL and MariaDB Support Alongside SQLite

Context and Problem Statement

📝 ADR-0003 chose SQLite as the embedded database for Spotter's initial personal-use deployment model. While SQLite remains appropriate for single-user homelab setups, Spotter is gaining multi-user deployments where SQLite's serialized write model creates contention: concurrent background tickers (sync, metadata, playlist sync) compete for the write lock, and users with large libraries experience noticeable slowdowns. Additionally, operators who already run PostgreSQL or MariaDB in their homelab want to integrate Spotter into their existing database infrastructure rather than manage a separate SQLite volume.

Decision Drivers

  • Multi-user deployments experience write contention under SQLite's serialized write model
  • Homelab operators with existing PostgreSQL or MariaDB infrastructure prefer to reuse it
  • The existing SPOTTER_DATABASE_DRIVER / SPOTTER_DATABASE_SOURCE config keys already express the intent for multi-driver support
  • Ent ORM already handles all three dialects (dialect.SQLite, dialect.Postgres, dialect.MySQL) — only the driver package imports are missing
  • SQLite must remain fully supported as the zero-infrastructure default for single-user deployments

Considered Options

  • Add PostgreSQL (lib/pq) + MariaDB (go-sql-driver/mysql) alongside SQLite — all three drivers compiled in, operator selects via SPOTTER_DATABASE_DRIVER
  • Replace SQLite with PostgreSQL as the only driver — breaking change, loses single-container simplicity
  • Add PostgreSQL only — leaves out MariaDB operators

Decision Outcome

Chosen option: Add all three drivers, because it preserves the zero-infrastructure SQLite default while enabling PostgreSQL and MariaDB for operators who want them. Ent ORM already abstracts the dialect differences; the code change is minimal (internal/database/db.go imports + startup validation).

Drivers Selected

DatabaseGo DriverReason
SQLitegithub.com/mattn/go-sqlite3 (existing)Embedded, zero-ops, personal use default
PostgreSQLgithub.com/lib/pqPure Go-compatible, stable, well-tested with Ent
MariaDB / MySQLgithub.com/go-sql-driver/mysqlStandard MySQL protocol driver, works with MariaDB 10.x+

Consequences

  • Good, because SQLite remains the default — single-container deployments require no changes
  • Good, because PostgreSQL and MariaDB operators can point Spotter at their existing infrastructure with two env var changes
  • Good, because Ent's Schema.Create() handles DDL migration for all three dialects
  • Good, because lib/pq and go-sql-driver/mysql are pure Go — no CGO required for PostgreSQL/MariaDB deployments
  • Bad, because CGO (go-sqlite3) is still required when using the SQLite driver — multi-stage Docker build remains necessary
  • Bad, because SPOTTER_DATABASE_SOURCE format differs per driver — operators must supply the correct DSN format
  • Neutral, because all three drivers are compiled into the binary regardless of which is configured at runtime (minimal binary size impact)

Confirmation

Compliance is confirmed by go.mod containing github.com/lib/pq and github.com/go-sql-driver/mysql, blank imports of both in internal/database/db.go, and internal/config/config.go validating the driver value against ["sqlite3", "postgres", "mysql"].

Default Connection Strings by Driver

DriverSPOTTER_DATABASE_DRIVERDefault SPOTTER_DATABASE_SOURCE
SQLitesqlite3file:spotter.db?cache=shared&_fk=1
PostgreSQLpostgreshost=localhost port=5432 dbname=spotter sslmode=disable
MariaDB/MySQLmysqlspotter:spotter@tcp(localhost:3306)/spotter?parseTime=true&charset=utf8mb4

More Information

  • Ent dialect constants: entgo.io/ent/dialectdialect.SQLite, dialect.Postgres, dialect.MySQL
  • Spec: docs/openspec/specs/multi-database-support/spec.md
  • Previous decision: [📝 ADR-0003](./📝 ADR-0003-sqlite-embedded-database) (superseded by this ADR)
  • ORM choice: [📝 ADR-0004](./📝 ADR-0004-ent-orm-code-generation) (Ent ORM — unchanged)