Skip to main content

Storage Backends

PraisonAI provides pluggable storage backends for training data, session state, and general persistence. Switch between file-based and database storage without changing your application code.

Supported Backends

Quick Start

Any Component with Any Backend

All storage components support pluggable backends. Create a backend once and use it with any component:

FileBackend

JSON file-based storage. Each key becomes a separate .json file.
Features:
  • Zero dependencies (uses built-in json)
  • Thread-safe with atomic writes — a failed save() (serialization error, encoding error, or os.replace failure) leaves the previous store intact and removes the staging .tmp file. See Thread Safety → BaseJSONStore atomic save.
  • Human-readable files
  • Easy debugging and inspection

Failure Modes

FileBackend.save is all-or-nothing: either the destination file is replaced with the new content, or it stays exactly as it was before the call. No partial files are left behind on any failure path, so re-running the save after fixing the data is safe — the storage directory never fills up with orphaned staging files.

Error Handling

load() never raises on a corrupted file — it logs a warning and returns None. Your agent keeps running and other keys keep loading.
load() is non-destructive — a corrupted file is left on disk so you can inspect or restore it. To clear a bad key, call backend.delete(key) explicitly.

Save-side guard: an unreadable key cannot be overwritten by ordinary saves

Once backend.load(key) observes a decode failure on a key (invalid UTF-8, malformed JSON, or IOError), the same backend instance refuses an ordinary backend.save(key, data) while the unreadable file is still on disk. Neighbouring, readable keys stay fully usable — the guard is per-path.
The guard clears when any of these happens: Writes to other keys are never affected.
This guard exists so a transient-looking fallback (load() → None) never silently clobbers forensic evidence on disk. Repair the file (restore, re-encode) or delete it explicitly before writing again.

SQLiteBackend

SQLite database storage. All data in a single .db file.
Features:
  • Zero dependencies (uses built-in sqlite3)
  • ACID transactions
  • Better concurrent access
  • Faster for large datasets
  • Single file deployment

Table name rules

table_name accepts letters, digits, and underscores only. The default is praison_storage. SQL keywords and digit-leading names are safe. The backend quotes the identifier internally ("select", "9table"), so names like select, order, 9table, or 123 work with nothing extra required.
Invalid characters are rejected at construction. Names with spaces, quotes, hyphens, or SQL-injection payloads raise ValueError before any SQL runs. SQLite itself also reserves object names starting with sqlite_, which it refuses regardless of this backend. Different table_name values map to different, isolated tables in the same .db file — useful for sharing one database across stores without separate files.
table_name is validated at construction and then used as a quoted SQLite identifier ("table_name") in every statement, so SQL keywords and digit-leading names are safe. Only names matching [a-zA-Z0-9_]+ are accepted; anything else raises ValueError before any SQL runs. SQLite’s own sqlite_ prefix restriction still applies — see Construction failure modes for what happens when a reserved name slips past validation.

Construction failure modes

When SQLiteBackend(...) raises during construction, the opened SQLite connection is closed before the error propagates, and the database file is left exactly as it was before the call. This makes construction safe to retry — inspect or replace the .db file, fix the offending argument, and reconstruct.
SQLiteBackend(..., auto_create=False) skips schema creation entirely, so none of these schema-init paths run. Validation of table_name still applies.

Using with Training Storage

TrainingStorage is a context manager — use with to close the backend deterministically.
Using with (or calling .close() explicitly) releases the underlying SQLite or Redis connection so the file can be deleted or reopened immediately — essential on Windows, and best practice for long-running processes. On backends without an open resource (e.g. FileBackend) .close() is a safe no-op.
When you use a SQLiteBackend (or RedisBackend) directly — for example via list_sessions_from_backend(SQLiteBackend(db_path=db)) — close the backend yourself, or wrap the call in try / finally. TrainingStorage’s context manager only closes backends it owns.

Using with Learn Stores

Async Support

For async applications, use AsyncBaseJSONStore:

Custom Backends

Implement StorageBackendProtocol for custom backends:

Protocol Reference

RedisBackend

Redis-based storage for high-speed caching and ephemeral data.
Features:
  • Sub-millisecond latency
  • Built-in TTL support
  • Automatic key prefixing
  • Requires redis package: pip install redis

Using with RunHistory (Recipe History)

Store recipe run history with pluggable backends:

Using with SessionManager (Session State)

Persist CLI session state with different backends:

Using with MCPToolIndex (Tool Index)

Store MCP tool schemas with pluggable backends:

Choosing a Backend

Backend Comparison

Database Overview

All supported databases

Memory Storage

Agent memory persistence

Training

Agent training with storage

Session Resume

Continue conversations