atoll PDS

Running a server

SQLite

Atoll supports PostgreSQL (the default) and SQLite for a single-node PDS. The Ecto adapter is selected at build time. Keep ATOLL_DATABASE=sqlite set for every Mix command, including release builds. Switching adapters requires recompilation and does not convert an existing database. SQLite uses its own _build/sqlite directory so adapter-specific compiled artifacts stay separate.

Development and tests#

export ATOLL_DATABASE=sqlite
mix setup
mix phx.server

Development uses atoll_dev.sqlite3 in the project directory. Tests use atoll_test.sqlite3 (with MIX_TEST_PARTITION appended to the filename when set). Override the filename with DATABASE_PATH; use a different file for each test partition if overriding it. Database files and journals are ignored by Git.

ATOLL_DATABASE=sqlite mix precommit
ATOLL_DATABASE=postgres mix precommit

SQLite sandbox cases run serially. Concurrency tests still run concurrent workers, which queue for SQLite’s single pooled connection. PostgreSQL-only replica, row-lock, and aborted transaction tests are excluded on SQLite.

Production#

Build with ATOLL_DATABASE=sqlite MIX_ENV=prod mix release. Set DATABASE_PATH=/absolute/path/to/atoll.sqlite3 at runtime; its parent directory must exist and be writable by the service. Run bin/atoll eval 'Atoll.Release.migrate()' before starting the release. All the identity, session, custody-key, and HTTPS settings in deploy.md still apply.

Use a persistent local filesystem, with one Atoll node owning the database. SQLite uses WAL, foreign keys, full synchronous durability, a single-connection pool, and a five-second busy timeout. Transactions begin with BEGIN IMMEDIATE to acquire the writer lock before reading authorization or mutation state. The pool queues contenders before they enter the native driver. SQLite has no equivalent of PostgreSQL's per-statement timeout. Long transactions serialize writers across the database, so PostgreSQL remains the choice for multiple nodes, replicas, or higher write concurrency. READ_DATABASE_URL is rejected with SQLite.

The existing ATOLL_BLOB_STORAGE=postgres value means database-backed blobs; with this adapter those blobs live in SQLite. S3 remains supported. Likewise, the existing postgres rate-limit backend value selects the primary database. These names are retained for configuration and stored-data compatibility.

Schema and backups#

SQLite migrations live in priv/sqlite_repo/migrations; PostgreSQL migrations remain in priv/repo/migrations. The SQLite baseline includes the schema through PostgreSQL migration 20260927072902, including check constraints, foreign keys, indexes, and transactional revision-reference triggers. Add migrations for both adapters when changing the schema. Generate SQLite migrations with ATOLL_DATABASE=sqlite mix ecto.gen.migration migration_name.

The PostgreSQL backup/recovery scripts in ops/backup do not back up SQLite. For a consistent live SQLite backup, use SQLite's backup command:

sqlite3 /absolute/path/to/atoll.sqlite3 ".backup '/backup/atoll.sqlite3'"

Alternatively, stop Atoll cleanly before copying its database files. Do not copy only the main database file while WAL writes are active. Back up encryption and session keys and any S3 objects with the database, as described in the deployment guide. SQLite and PostgreSQL databases are not interchangeable restore formats.

Type to search across every page.