Choosing a State Persistence Backend for Reflex Applications: In‑Memory, SQLite, or Redis
Decide which Reflex state backend— in‑memory, SQLite, or Redis—fits your app’s durability, scalability, and complexity needs. A concise decision guide, comparison table, and hands‑on config example help you pick the right persistence strategy.
29 Sept 2025, 15:44 UTC

Problem & Takeaway
When building a Reflex app, you must decide how the framework should persist UI state between requests and across server restarts. Reflex offers three built‑in backends:
- In‑memory – fastest, no external service, but data disappears on restart.
- SQLite – file‑based durable storage, zero‑config, suitable for single‑node deployments.
- Redis – shared, high‑performance store that enables horizontal scaling and fault tolerance.
The right choice depends on your durability needs, traffic patterns, and deployment topology. This guide presents the decision space, a compact comparison table, and a concrete configuration change that swaps backends with minimal code.
Decision Context
Ask yourself:
- Do I need state to survive a server restart?
- Will I run multiple Reflex instances behind a load balancer?
- Is the application low‑traffic or high‑concurrency?
- Can I tolerate the overhead of an external service?
- What security controls (TLS, authentication) are required for the state store?
Comparison Table
| Feature | In‑Memory | SQLite | Redis |
|---|---|---|---|
| Speed | Fastest – no network hop | Fast, but limited by disk I/O | Fast, network latency < 1 ms in‑data‑center |
| Durability | None – lost on process exit | Durable on disk, survives restarts | Durable if persistence is enabled (RDB/AOF) |
| Multi‑Instance Support | No – each instance has its own copy | No – each instance has its own file | Yes – shared keyspace across instances |
| External Service Required | No | No – file on local filesystem | Yes – Redis server or cluster |
| Setup Complexity | Zero | Zero – just a file path | Medium – install or provision Redis, configure auth/TLS |
| Concurrency Safety | Single‑process only | SQLite supports concurrent reads, but write locks can block | Thread‑safe, handles many concurrent writes |
| Cost | $0 | $0 (local disk) | Depends on Redis deployment (managed or self‑hosted) |
| Security Considerations | None – data in RAM only | File permissions control access; encrypted disk optional | Requires TLS, auth, and network isolation in production |
Trade‑Off Summary
- In‑Memory is ideal for development, prototyping, or low‑traffic single‑user apps where state loss is acceptable.
- SQLite offers a good balance for small to medium sites that run on a single server and need durable state without external dependencies.
- Redis shines when you need shared state across a fleet of Reflex workers, high write concurrency, or when you already run Redis for caching or pub/sub.
Concrete Implementation
Reflex abstracts the backend via the State class. Switching persistence only requires editing reflex/config.py and optionally installing the required package. Below is a minimal config.py for each backend.
In‑Memory (default)
# reflex/config.py
from reflex import State
class AppState(State):
# Define your state variables here
counter: int = 0
# No additional config needed – Reflex uses a process‑local dict
SQLite Persistence
# reflex/config.py
from reflex import State
class AppState(State):
counter: int = 0
# Tell Reflex to use SQLite and point to a file
state_manager = {
"backend": "sqlite",
"path": "./state.db", # relative or absolute path
}
After adding the state_manager dict, run reflex run. Reflex will create state.db automatically. If you need concurrent writes, enable Write‑Ahead Logging (WAL) by adding "sqlite_wal": True to state_manager.
Redis Persistence
# reflex/config.py
from reflex import State
class AppState(State):
counter: int = 0
# Redis configuration – adjust host, port, and auth as needed
state_manager = {
"backend": "redis",
"url": "redis://localhost:6379/0", # or use REDIS_URL env var
"prefix": "reflex:appstate", # optional; defaults to reflex:appstate
"ssl": False, # set True if connecting via TLS
"password": None, # set if Redis requires auth
}
Ensure a Redis server is reachable before starting the app. Reflex will serialize state objects as JSON and store them under the prefix key.
Verification Steps
- Run the app:
reflex run.- Interact with the UI to modify state (e.g., click a button that increments
counter). - Observe the console; Reflex logs state changes when the backend is not in‑memory.
- Interact with the UI to modify state (e.g., click a button that increments
- Restart the process.
- With
in‑memory, the counter resets to0. - With
SQLiteorRedis, the counter value persists.
- With
- Inspect storage (SQLite).
- Open
state.dbwithsqlite3 state.dband runSELECT * FROM _state;to see the serialized object.
- Open
- Inspect storage (Redis).
- Use
redis-cli -a <password>and runkeys reflex:*to list state keys. - Run
GET reflex:appstateto view the JSON blob.
- Use
- Scale test (Redis only).
- Deploy two instances behind a load balancer.
- Increment the counter from one instance; refresh the other instance – the value should match immediately.
These checks confirm that the chosen backend behaves as expected for durability and cross‑instance visibility.
Practical Tips
- For SQLite under high concurrency, enable WAL mode:
sqlite_wal = Trueinstate_manageror runPRAGMA journal_mode=WAL;after the database is created. - When using Redis, consider deploying a managed service that provides TLS and authentication out of the box. Configure
ssl=Trueand supplypasswordin the URL. - Always back up the
state.dbfile if you rely on SQLite for critical data. - Monitor Redis memory usage; if state objects grow large, set an eviction policy or use Redis Cluster.
By evaluating your app’s durability, scalability, and operational constraints against the table above, you can select the persistence backend that delivers the right balance of simplicity, performance, and resilience.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.