---
title: 'Persistence'
slug: persistence
description: 'Which storage backends each stateful Mochi feature supports — in-memory, file, SQLite, Postgres, or your own store.'
---
## Persistence
A few Mochi features keep server-side state that outlives a single request — queued jobs, cached values, rate-limit counters, spent captcha nonces. Each one defaults to in-memory storage and can be pointed at something durable instead.
### Per feature
- **[Queues](/docs/queues/)** — in-memory by default. Point the serve-level `queueStorage` (or a descriptor's `storage`) at a SQLite file (`{ sqlite: path }`), a Postgres database (`{ postgres: url }`), or an embedded PGlite instance (`{ pglite }`). One store serves every queue; Postgres storage is shared, so multiple processes can work one backlog.
- **[Cache](/docs/cache/)** — `new MochiCache({ storage })`. Defaults to `MemoryStorage`; `FileStorage` writes one JSON file per entry. Built-in SQLite and Postgres backends are planned; until then any other backend is a `Storage` implementation (`getItem` / `setItem` / `removeItem` / `clear`), with `serialize` / `deserialize` when the backend needs strings.
- **[Image cache](/docs/images/)** — the one feature that persists by default: `FileStorage` under `cacheDir`. Pass `image: { storage }` to swap it (e.g. `new MemoryStorage()`); `cacheDir` is then ignored.
- **[Rate limiting](/docs/rate-limiting/)** — `rateLimit: { store }`. `memoryStore()` (default), `sqliteStore({ path })` and `postgresStore({ url })` all ship with the framework; `MochiRateLimitStore` is the interface for your own. Create the store once and share the instance across routes.
- **[Captcha](/docs/captcha/)** — `captcha: { store: 'memory' | 'sqlite' }`, plus `storePath` for the SQLite file. A custom `NonceStore` needs only `consume(nonce, expiresAt)`.
### Choosing a backend
| Backend | Survives restart | Shared across instances | Use it when |
| ------------- | ---------------- | ----------------------- | --------------------------------------------------------------- |
| **In-memory** | No | No | Development, single-process apps, state that's cheap to rebuild |
| **File** | Yes | Only on shared storage | Caches whose entries are large blobs (images, API responses) |
| **SQLite** | Yes | Only on shared storage | One host, one process — durability without another service |
| **Postgres** | Yes | Yes | Several instances behind a load balancer |
In-memory state is **per process**. Two instances of your app each get their own rate-limit counters and their own spent-nonce set, so limits effectively multiply and a captcha nonce can be replayed once per instance. Pick a shared backend before you scale out.