Storage
Configure the storage backend for Chronicle Server.
Storage types
Section titled “Storage types”| Type | Description |
|---|---|
MongoDB | MongoDB (default) |
PostgreSql | PostgreSQL |
MsSql | Microsoft SQL Server |
Sqlite | SQLite (embedded, file-based) |
InMemory | In-memory (ephemeral, non-durable) |
MongoDB
Section titled “MongoDB”{ "storage": { "type": "MongoDB", "connectionDetails": "mongodb://localhost:27017" }}Database-name prefix
Section titled “Database-name prefix”Set storage.databaseNamePrefix to isolate Chronicle’s databases on a shared MongoDB server:
{ "storage": { "type": "MongoDB", "connectionDetails": "mongodb://localhost:27017", "databaseNamePrefix": "staging_" }}The prefix is prepended verbatim to every database name, including the global chronicle+main database, event stores, namespaces, read models, Orleans reminders, cluster membership, and jobs. For example, chronicle+main becomes staging_chronicle+main, and Orders+es+Default becomes staging_Orders+es+Default. All nodes in a cluster must use the same prefix.
The default is an empty string: existing database names remain unchanged. Changing the prefix selects different databases; it does not rename or migrate existing data. The complete name, including the prefix, must satisfy MongoDB’s naming rules and 63-byte limit. Choose a short prefix without spaces or MongoDB’s forbidden characters.
For environment-variable configuration, use Cratis__Chronicle__Storage__DatabaseNamePrefix. The development-only kernel reset preserves the global cluster database and, when a prefix is set, drops only other databases starting with that exact, case-sensitive prefix. Use a unique prefix when sharing a server; a prefix is a naming boundary, not a substitute for database permissions.
PostgreSQL
Section titled “PostgreSQL”{ "storage": { "type": "PostgreSql", "connectionDetails": "Host=localhost;Port=5432;Database=chronicle;Username=chronicle;Password=secret" }}Microsoft SQL Server
Section titled “Microsoft SQL Server”{ "storage": { "type": "MsSql", "connectionDetails": "Server=localhost;Database=chronicle;User Id=chronicle;Password=secret;TrustServerCertificate=True" }}Passwordless Azure authentication
Section titled “Passwordless Azure authentication”On Azure SQL, use a Microsoft Entra ID mode in the connection string instead of a password, for example Authentication=Active Directory Managed Identity;User Id=<client-id>. On Azure Database for PostgreSQL, have a connection resolver return an Entra access token as the identity. The supported modes, and what not to combine with a resolved token, are in Microsoft Entra ID authentication.
SQL database names
Section titled “SQL database names”PostgreSQL and SQL Server isolate event stores, namespaces and projected read models in separate databases. The configured cluster database supplies the prefix:
| Identity | Logical database name |
|---|---|
| Event store | <cluster>+es+<store> |
Namespace, including Default | <cluster>+es+<store>+<namespace> |
| Default-namespace read models | <cluster>+<store> |
| Named-namespace read models | <cluster>+<store>+<namespace> |
Names within the provider’s limit stay unchanged: 63 UTF-8 bytes for PostgreSQL, 128 UTF-16 code units for SQL Server. Longer names use a readable prefix, ~, and the first 16 lowercase hexadecimal characters of the SHA-256 of the full logical name in UTF-8. The prefix ends at a Unicode character boundary. The configured cluster name is never shortened; startup rejects it if it exceeds the limit.
Permanent bindings
Section titled “Permanent bindings”The cluster’s DatabaseNames table records each identity’s physical database name. Short names are bound too. Its logical key is (Kind, EventStore, Namespace); Namespace stores Default explicitly, including for event store identities. PhysicalName is unique under PostgreSQL’s ordinal equality or SQL Server’s server collation. The configured cluster database has a reserved row with Kind = 0, an empty EventStore, and Namespace = 'Default'.
| Kind | Identity |
|---|---|
0 | Cluster |
1 | Event store |
2 | Namespace |
3 | Read models |
Bindings survive restarts and development wipes. Adding a namespace cannot move an existing database. A namespace is bound before it is made available; another kernel can reload that binding when resolving jobs storage. An unbound jobs identity fails instead of guessing a database name.
For an existing PostgreSQL deployment, Chronicle preserves a legacy truncated database only where ownership is clear. An event store takes precedence over its own namespaces and read models; default-namespace read models take precedence over that store’s named-namespace read models. Unrelated stores or namespaces have no precedence. A losing identity gets a separate hashed database, with a warning, only if the shared database contains no schema or migration history that could belong to it.
Resolve shared legacy ownership
Section titled “Resolve shared legacy ownership”DatabaseOwnershipUnresolved names the identity, shared database and competing claimants. Do not delete migration history or repeatedly restart the kernel: that can strand event history in the old database while new writes go elsewhere.
- Stop every kernel using this cluster database and take a recoverable backup of the cluster and shared database.
- Identify which tables and rows belong to the reported identity. Event store metadata, namespace events/constraints/observer state/jobs, and projected read models have different owners. If two identities’ rows are mixed in the same table, separate them from verified business identity information before proceeding; do not copy another identity’s data.
- Create a separate target database with a valid physical name, on the same server and with the required permissions. Use the computed hashed name or another unused name within the provider limit.
- Copy the identity’s tables, data, indexes, constraints and any required dependencies. Copy only its
__EFMigrationsHistoryrows: a namespace owns the rows starting withNS-and the jobs rows starting withCratis-Orleans-Jobs-; every other row belongs to the event store. Read model databases normally have no EF migration history. Preserve the original database until recovery is verified. - Insert the identity’s binding into the cluster’s
DatabaseNamestable. Use its exact store and namespace, the kind from the table above and the target physical name. Do not update an existing binding. Both unique constraints must hold; a duplicate is a conflict to investigate, not a row to overwrite. - Restart the kernels. Chronicle honors the inserted binding. Confirm migrations complete and the identity’s events, jobs and read models are reachable before retiring any original data.
For example, bind a namespace named TenantA in store Orders to a database you have already created and populated. On PostgreSQL, run this in the cluster database:
INSERT INTO "DatabaseNames" ("Kind", "EventStore", "Namespace", "PhysicalName")VALUES (2, 'Orders', 'TenantA', 'orders_tenant_a_recovered');On SQL Server, the equivalent insert is:
INSERT INTO [DatabaseNames] ([Kind], [EventStore], [Namespace], [PhysicalName])VALUES (2, N'Orders', N'TenantA', N'orders_tenant_a_recovered');DatabaseMigrationIncomplete is a different failure: an object already exists but EF migration history still has pending migrations. The error names the database and first pending migration. Repair the schema/history mismatch from a backup and verified schema evidence; Chronicle does not treat duplicate objects as proof that migration completed.
Platform-managed databases
Section titled “Platform-managed databases”When a platform decides where each event store and namespace lives, configure storage connection resolution to resolve the server, database, schema and identity per namespace and purpose. createIfMissing: false stops Chronicle from creating databases, and schema keeps its tables in the schema you grant.
SQLite
Section titled “SQLite”{ "storage": { "type": "Sqlite", "connectionDetails": "Data Source=/data/chronicle.db" }}In-memory
Section titled “In-memory”The in-memory backend keeps every event sequence, observer, read-model sink, and system record in process memory. There is no connection string, and connectionDetails is ignored.
{ "storage": { "type": "InMemory" }}When you host Chronicle programmatically, select the in-memory backend with the WithInMemory() builder extension instead of configuration:
siloBuilder.AddChronicleToSilo(chronicle => chronicle.WithInMemory());Kotlin does not support this workflow yet.Java does not support this workflow yet.Elixir does not support this workflow yet.TypeScript does not support this workflow yet.| Property | Type | Required | Description |
|---|---|---|---|
| type | string | Yes | Storage type: MongoDB, PostgreSql, MsSql, Sqlite, or InMemory |
| connectionDetails | string | Conditional | Connection string for the chosen storage backend. Required for all backends except InMemory, which ignores it |
| databaseNamePrefix | string | No | Prefix prepended to every MongoDB database name. Defaults to empty; ignored by other storage backends |
| schema | string | No | SQL schema Chronicle keeps its tables in. Defaults to empty, the database user’s default schema; ignored by MongoDB and SQLite |
| createIfMissing | boolean | No | Whether Chronicle creates missing databases and schemas. Defaults to true |
| connectionResolution | object | No | Resolves the server, database, schema and identity per event store, namespace and purpose. See storage connection resolution |