Database and Migrations
OpenReader keeps accounts, settings, document records and reading progress in SQL. File contents and generated audio live in object storage.
SQLite or Postgres
| Database | When it is used | Good for |
|---|---|---|
| SQLite (default) | POSTGRES_URL is unset. The file is docstore/sqlite3.db; set SQLITE_DB_PATH to move it. | A single server, Docker Compose, the desktop app. |
| Postgres | POSTGRES_URL is set. | Production, several app instances, Vercel. |
What it stores
| Data | Tables |
|---|---|
| Accounts and sessions (generated by Better Auth) | user, session, account, verification |
| Documents, folders and their settings | documents, user_folders, document_settings, document_blob_leases |
| Reading progress and bookmarks | user_document_progress, user_document_bookmarks |
| Preferences and onboarding | user_preferences, user_onboarding |
| Previews (status and references, not images) | document_previews |
| Admin providers and settings | admin_providers, admin_settings |
| Compute limits and usage | compute_limit_admissions, compute_limit_buckets, compute_limit_events |
| Scheduled tasks | scheduled_tasks |
Document files, generated audio, audiobooks and preview images are not in the database. Playback state belongs to the compute worker, which never connects to the database.
Settings, progress and bookmarks sync between devices when the app loads or saves them. There is no live push between open tabs.
Migrations
Migrations run automatically every time OpenReader starts, through Docker, pnpm start, pnpm dev or the desktop app. Startup runs two steps:
- Database migrations (
pnpm migrate) for whichever database is configured. - v4 storage cleanup (
pnpm migrate-decommission), which deletes storage folders retired in v5. It does nothing once they are gone.
The startup log shows Running database migrations... and Running v4 legacy storage decommission....
To skip either step, set RUN_DRIZZLE_MIGRATIONS=false or RUN_V4_DECOMMISSION=false. Then run them yourself before serving traffic.
:::warning Vercel and other custom deployments
Deployments without the bootstrap entrypoint, including Vercel, do not run migrations on start. Run pnpm migrate against the production database before deploying a new version. See Vercel + External Worker.
:::
Upgrading from v4 runs irreversible migrations and deletes old audio. Follow Upgrade from v4.
Schema history
Migrations apply in order to both SQLite and Postgres. A v4.4 database keeps its users, documents, progress, folders, preferences, providers and settings when upgraded.
| Migration | What it does |
|---|---|
0000 | Baseline documents, preferences, progress, auth, previews and legacy audiobooks. |
0001–0003 | The pre-v5 TTS segment cache. |
0004 | Encrypted shared providers, admin settings and admin users. |
0005–0009 | Document settings and transitional PDF state, later removed. |
0010 | Cleanup cascades for user data. |
0011 | Scheduled tasks and document blob leases. |
0012 | Folders, onboarding and recently opened documents. |
0013–0014 | Transitional playback sessions used during v5 development. |
0015 | Removes the pre-v5 TTS cache, transitional playback sessions and legacy audiobook tables. |
0016 | Records which identity provider issued each sign-in account. |
0017–0019 | Compute admissions, usage metering and the maintained limit policy. |
0020 | User approval status and admin grant source; converts the old sign-up switch to signupPolicy. |
0021 | Bookmarks, document author and language, and reading positions saved as playback positions. |
To create new migrations when changing the schema, see Local Development.