Understanding storage backends¶
Zope and Plone use ZODB (the Zope Object Database) to persist Python objects.
ZODB is storage-agnostic – the actual persistence layer is pluggable. This
page explains the four storage backends supported by
cookiecutter-zope-instance, when to use each, and the trade-offs involved.
The four backends at a glance¶
Backend |
Best For |
Horizontal Scaling |
Undo Support |
External Dependencies |
|---|---|---|---|---|
direct (filestorage) |
Development, single-process |
No (single process only) |
Yes (built-in) |
None |
relstorage (PostgreSQL, MySQL, Oracle, SQLite) |
Production, horizontal scaling |
Yes (multiple app processes) |
Optional (history-preserving mode) |
RDBMS server |
zeo |
Production, moderate scaling |
Yes (multiple app processes) |
Yes (built-in) |
ZEO server process |
pgjsonb |
Cloud-native, SQL-queryable ZODB |
Yes (multiple app processes) |
Optional (history-preserving mode) |
PostgreSQL 14+ |
Direct filestorage¶
Direct filestorage is the simplest backend. It stores all object data in a
single Data.fs file on the local filesystem. No external database server is
required.
When to use it:
Local development
Single-process deployments (one Zope/Plone instance)
Quick prototyping and testing
Limitations:
Only one operating system process can open a filestorage at a time. If you need to run multiple Zope application processes for load balancing, you must use one of the other backends.
Filestorage is history-preserving by nature: every transaction is appended to the file. The file grows over time and must be packed periodically to reclaim space from old revisions.
RelStorage¶
RelStorage stores ZODB object pickles in a relational database. PostgreSQL is the recommended RDBMS, but MySQL, Oracle, and SQLite are also supported.
When to use it:
Production environments that need horizontal scaling (multiple Zope processes sharing one database)
Environments where you already have a well-managed RDBMS
When you need mature, battle-tested multi-process ZODB
Architecture:
Each Zope application process connects directly to the relational database. Object pickles are stored as binary data in database rows. RelStorage handles conflict resolution, garbage collection, and (optionally) history preservation.
Trade-offs:
Requires an RDBMS server to be provisioned and maintained
Blob handling requires choosing between cache mode (blobs in the RDBMS, cached locally) or shared mode (blobs on a shared filesystem)
History-free mode gives better performance but disables ZODB-level undo
ZEO (Zope enterprise objects)¶
ZEO is the original client-server protocol for ZODB. A dedicated ZEO server process manages the filestorage, and multiple Zope client processes connect to it over TCP.
When to use it:
Production environments where you prefer a pure-Python, ZODB-native solution without an external RDBMS
Deployments where shared NFS blob storage is readily available
Existing infrastructure already running ZEO
Architecture:
The ZEO server holds the actual filestorage and mediates access. Each Zope client maintains a local object cache (in memory and optionally on disk) for performance. Invalidation messages keep client caches consistent.
Trade-offs:
The ZEO server is a single point of failure (though you can configure failover addresses)
Blob sharing typically requires a network filesystem (NFS)
Somewhat lower throughput than RelStorage under heavy write loads
PGJsonb¶
zodb-pgjsonb is a newer storage adapter that stores ZODB object state as PostgreSQL JSONB columns rather than opaque binary pickles. A Rust-based codec (zodb-json-codec) transcodes between Python pickle format and JSON.
When to use it:
Cloud-native deployments where a managed PostgreSQL service (RDS, Cloud SQL, AlloyDB, etc.) replaces local filestorage and blob directories
When you want to query ZODB data directly with SQL/JSONB operators
Modern PostgreSQL-centric architectures
Integration with tools that consume JSON natively (analytics, reporting)
Architecture:
Object state is transcoded from pickle to JSON on write, and from JSON back
to pickle on read. The JSON representation is stored in a PostgreSQL JSONB
column, making it indexable and queryable. Blobs are stored in PostgreSQL
bytea columns by default, with optional tiering to S3-compatible object
storage for large blob volumes.
Trade-offs:
Requires PostgreSQL 14 or later
Transcoding adds CPU overhead on every read/write (mitigated by the Rust implementation)
Newer and less battle-tested than RelStorage or ZEO
Blob handling is fundamentally different from the other backends
S3 blob wrapper (z3blobs)¶
zodb-s3blobs is not a fifth storage
backend but a wrapper that can be applied to any of the first three backends
(direct, relstorage, or zeo). When enabled, it intercepts all blob
operations and redirects them to S3-compatible object storage, maintaining a
local LRU cache for performance.
When to use it:
Deployments where local or shared filesystem blob storage is impractical
Containerized environments where blob data must survive container restarts
When you want to offload large blob volumes to object storage
How it works:
The selected backend (e.g. filestorage or RelStorage) handles all non-blob ZODB operations normally. The z3blobs wrapper takes over blob reads and writes, storing blobs in an S3 bucket and caching recently accessed blobs locally. The inner storage’s own blob directives are suppressed automatically.
Not compatible with PGJsonb:
PGJsonb handles blobs natively via PostgreSQL bytea with optional S3 tiering.
Wrapping PGJsonb with z3blobs is a configuration error and will be rejected
during instance generation.
Choosing a backend¶
For most teams, the decision comes down to:
Development or single-process? Use
direct.Need multi-process with an existing RDBMS? Use
relstorage.Need multi-process, prefer ZODB-native simplicity? Use
zeo.Cloud-native or want SQL-queryable ZODB data on PostgreSQL? Use
pgjsonb.Need S3 blob storage with direct, relstorage, or zeo? Enable
db_z3blobs_enabled.
All four backends support the same ZODB API, so switching between them is a
configuration change plus a one-time data migration. The
zodb-convert tool handles this
migration generically – it can copy data between any two ZODB-compatible
storages. Because cookiecutter-zope-instance generates a complete
zope.conf for each backend, zodb-convert can read the storage
configuration directly from these files. See Migrate data between storage backends
for a step-by-step guide.
Your application code does not need to change.