---
url: https://radioactive-labs.github.io/plutonium-core/reference/generators/lite.md
---
# Lite (SQLite) Generators

The `pu:lite:*` generators configure a SQLite-first production stack. This page
covers the two tuning/maintenance generators; the solid\_queue / solid\_cache /
solid\_cable / solid\_errors / litestream / rails\_pulse generators are run the
same way (`rails g pu:lite:<name>`).

## `pu:lite:tune`

Adds tuned performance pragmas to the `default: &default` block of
`config/database.yml`.

```bash
rails g pu:lite:tune
```

It writes a `pragmas:` mapping:

* `cache_size: -64000` — 64 MB page cache (the ~2 MB default is too small).
* `temp_store: 2` — MEMORY; sorts and temp indexes stay off disk.
* `mmap_size: 536870912` — 512 MB memory-mapped I/O.
* `wal_autocheckpoint: 10000` — checkpoint roughly every 40 MB of WAL.

On Rails < 8.1 it also writes the baseline pragmas (`journal_mode: WAL`,
`synchronous: NORMAL`, `foreign_keys: true`, `journal_size_limit`) that Rails 8.1+
already sets by default.

**Why no `busy_timeout`?** Rails routes the `timeout:` key to the sqlite3 gem's
constant-poll busy handler (`busy_handler_timeout`), which has better tail-latency
than SQLite's internal exponential backoff. Setting a busy-timeout pragma would
replace the better handler with the worse one, so this generator never emits it.

The generator is idempotent — re-running it detects the existing pragmas and skips.
It only ever touches the `default:` block, so a `pragmas:` mapping nested under
another environment is left untouched.

## `pu:lite:maintenance`

Installs `app/jobs/sqlite_maintenance_job.rb` and (when `solid_queue` is present)
schedules it in `config/recurring.yml`.

```bash
rails g pu:lite:maintenance
# custom schedule:
rails g pu:lite:maintenance --schedule="every day at 4am"
```

The job runs `PRAGMA optimize` on every configured SQLite database and `VACUUM`
only on databases without live 24/7 writers (`primary`, `errors`, `rails_pulse`
by default — edit `VACUUM_DBS` in the generated job to suit your app).

**Why VACUUM only some databases?** SolidQueue, Solid Cache and Solid Cable write
to their databases constantly. `VACUUM` takes a global *exclusive* lock for its
whole duration, which stalls and errors those processes (e.g. SolidQueue process
deregistration failing with "database is locked"). They also barely benefit: in
WAL mode, freed pages land on the freelist and are reused, so a churning database
stays at a steady-state size without nightly reclamation. `PRAGMA optimize`, which
only takes a brief shared lock, still runs everywhere.

Databases listed in the job that don't exist in `config/database.yml` are skipped
at runtime, so the same job is safe regardless of which `pu:lite:*` generators you
have run.

If `solid_queue` is not installed, the job file is still created but not scheduled —
add a `sqlite_maintenance` entry to whatever scheduler you use.
