What is SqliteProvider?
SqliteProvider is the default storage backend that CrewAI Flows use to persist state whenever a Flow class is decorated with @persist. Under the hood it opens a SQLite database file (defaults to ~/.crewai/flows/<flow_name>.db unless you pass db_path to SqliteProvider) and writes one row per checkpoint, keyed by the Flow's state_id. The shape on disk is intentionally boring: a single state table with id, payload (JSON), and updated_at columns — which makes inspection with the sqlite3 CLI trivial and rules out vendor lock-in.
Why SQLite as a default? It gives you ACID semantics, file-level durability, and concurrent reader support without forcing a database dependency on first-time users. Connections are opened in WAL mode so multiple Flow runs on the same machine can read state while one writer commits, which is enough to handle the burst-y write pattern of @persist (one write per @start / @listen completion). Writes are serialized — SQLite uses a single writer at a time — but the @persist call path is small enough that this is rarely a bottleneck below a few hundred flows per second.
The boundaries to know: SqliteProvider is single-host. The database file lives on local disk, so two machines cannot share the same SqliteProvider instance via NFS or EFS without risking corruption (network filesystems do not honor SQLite's locking primitives reliably). For multi-pod Kubernetes deployments or any horizontally scaled worker pool, swap in a custom StorageBackend backed by Postgres, Redis, or your existing state store via CheckpointConfig(storage=...). For single-host production — a long-running container, a VM, a Cloud Run job with persistent volume — SqliteProvider is the right answer and the one the CrewAI team has tuned the default behavior for.
When to Use
Single-host production flows, long-running CrewAI Flows that survive process restarts, and any @persist usage where you don't want to stand up a separate database.
Use Cases
- • Single-host production CrewAI Flow deployments
- • Long-running flows that span hours or days
- • Local development with persisted checkpoints between runs
- • Cron-driven flows that resume from the last completed step
- • Cloud Run / ECS tasks with a persistent volume
Key Features
- ✓ ACID guarantees (atomic checkpoint writes)
- ✓ Embedded — zero external dependencies
- ✓ WAL mode for concurrent readers
- ✓ Inspectable with the standard sqlite3 CLI
- ✓ Configurable db_path
- ✓ Restore-by-state-id semantics for resumable flows
When NOT to Use
Distributed deployments where multiple workers must share Flow state — use a custom StorageBackend backed by Postgres, Redis, or DynamoDB instead. Also avoid network filesystems (NFS, EFS) for the db file.
Notes
Where the database file actually lives
By default the file is created at ~/.crewai/flows/<flow_class_name>.db. In containers, this directory is ephemeral — mount a persistent volume and pass db_path explicitly, otherwise every container restart loses all checkpoints. The directory is created lazily on the first @persist write.
WAL mode and concurrent access
Connections are opened in WAL (Write-Ahead Logging) mode, so a concurrent reader (e.g. an admin dashboard inspecting state) won't block a Flow that is committing a checkpoint. SQLite still serializes writers — if you run dozens of flow workers against the same db file, you may see write contention. At that scale, move to a server-class database via a custom StorageBackend.
Backups are just file copies
Because the storage layer is a single SQLite file, your backup story is trivial — cp, rsync, or volume snapshots all work. Do not back up while a writer is active; use VACUUM INTO 'backup.db' from the sqlite3 CLI for a consistent online backup, or copy the .db file alongside the .db-wal and .db-shm files atomically.
When to upgrade to a real database
Switch to a custom StorageBackend (Postgres, Redis, DynamoDB) when any of these are true: more than one host runs the same flow, you need point-in-time recovery, the db file is approaching ~10 GB, or compliance requires audit logging at the storage layer. SqliteProvider is intentionally a sensible default — not a forever-home for multi-tenant production.
Import
from crewai.state import SqliteProvider
Key Parameters
| Parameter | Type | Default | Purpose |
|---|---|---|---|
| db_path | str | Path | ~/.crewai/flows/<flow>.db | Override where the SQLite file is created. Useful for containers with mounted volumes. |
| table_name | str | state | Name of the table used to store checkpoint rows. |
Code Examples
Default usage with @persist
from crewai.flow.flow import Flow, start, listen
from crewai.state.checkpoint_config import CheckpointConfig, persist
from crewai.state import SqliteProvider
@persist(config=CheckpointConfig(storage=SqliteProvider()))
class ResearchFlow(Flow):
@start()
def gather(self):
return fetch_sources()
@listen(gather)
def summarize(self, sources):
return summarize_sources(sources)
Custom db path for a containerized deployment
from pathlib import Path
from crewai.state import SqliteProvider
from crewai.state.checkpoint_config import CheckpointConfig
storage = SqliteProvider(db_path=Path('/data/flow_state.db'))
config = CheckpointConfig(storage=storage)
Resume a flow from a known state_id
flow = ResearchFlow()
result = flow.kickoff(inputs={'topic': 'observability'})
print('state_id =', flow.state.id)
restored = ResearchFlow()
restored.restore_from_id(flow.state.id)
restored.kickoff()
Inspect persisted state with the sqlite3 CLI
$ sqlite3 ~/.crewai/flows/ResearchFlow.db
sqlite> .schema state
CREATE TABLE state (id TEXT PRIMARY KEY, payload TEXT, updated_at REAL);
sqlite> SELECT id, updated_at FROM state ORDER BY updated_at DESC LIMIT 5;
Common Mistakes
❌ Sharing the .db file across machines via a network filesystem (NFS, EFS, GCS Fuse)
✅ Network filesystems break SQLite's file locking. Use a custom StorageBackend backed by Postgres or Redis for distributed setups.
❌ Leaving the default db_path inside a container without a mounted volume
✅ Pass db_path=Path('/data/flow.db') and mount /data as a persistent volume so checkpoints survive container restarts.
❌ Copying the .db file while a Flow is mid-write
✅ Use VACUUM INTO from the sqlite3 CLI for an online backup, or copy the .db + .db-wal + .db-shm files atomically.
SqliteProvider FAQ
What is SqliteProvider in CrewAI?
Default CrewAI Flow state backend — embedded SQLite for durable, concurrent-safe persistence of @persist flows. SqliteProvider is the default storage backend that CrewAI Flows use to persist state whenever a Flow class is decorated with @persist. Under the hood it opens a SQLite database file (defaults to ~/.crewai/flows/<flow_name>.db unless you pass db_path to SqliteProvider) and writes one row per checkpoint, keyed by the Flow's state_id. The shape on disk is intentionally boring: a single state table with id, payload (JSON), and updated_at columns — which makes inspection with the …
Which package defines the CrewAI class SqliteProvider?
DevShelfHub maps SqliteProvider to Python module crewai.state (package path crewai.state in this reference). Pin your installed crewai version and match imports to the snippet on this page.
When should I use SqliteProvider?
Single-host production flows, long-running CrewAI Flows that survive process restarts, and any @persist usage where you don't want to stand up a separate database.
When should I avoid using SqliteProvider?
Distributed deployments where multiple workers must share Flow state — use a custom StorageBackend backed by Postgres, Redis, or DynamoDB instead. Also avoid network filesystems (NFS, EFS) for the db file.
How do I import SqliteProvider in Python?
from crewai.state import SqliteProvider
Where can I explore more CrewAI API reference pages?
Open the CrewAI API reference index on DevShelfHub to search 58 classes, 30 methods, and 16 decorators, each with runnable examples, parameters, common mistakes, and cross-links.