On this page

On this page

Technical reference

Database engines (experimental)

OpenClaw uses SQLite by default. The experimental PostgreSQL adapter lets one Gateway keep supported stores in PostgreSQL while other stores stay on SQLite. The adapter owns connection setup, schema admission, transaction behavior, and commit notifications; stores keep their existing data and operation contracts.

Configure PostgreSQL

Use a PostgreSQL database dedicated to OpenClaw and a role that can create schemas and tables. Put its connection DSN in a secret, then configure a SecretRef:

json5
{  database: {    engine: "postgres",    postgres: {      connection: { source: "env", provider: "default", id: "OPENCLAW_DATABASE_DSN" },      schemaPrefix: "openclaw",    },  },}

Provide OPENCLAW_DATABASE_DSN in the Gateway's environment through your secret manager. It is the secret referenced by this example, not an engine-selection environment variable. File, exec, and secret-store references are also supported. The host resolves the secret and passes the connection to database workers; workers do not resolve SecretRefs. Treat the DSN as a credential and do not put it in logs or diagnostic captures.

Restart the Gateway after changing the engine, then run openclaw doctor to see each store's selected engine. Doctor warns that PostgreSQL is experimental and that stores without a PostgreSQL schema remain on SQLite.

Field Default Meaning
database.engine "sqlite" "sqlite" or "postgres". PostgreSQL applies to every registered store that supplies a PostgreSQL schema.
database.postgres.connection None Required for PostgreSQL. A DSN string or SecretRef; prefer a SecretRef.
database.postgres.schemaPrefix "openclaw" Prefix for new store schemas: 1–27 lowercase letters, digits, or underscores, starting with a letter or underscore. Existing anchors keep their recorded schema.

Workboard is currently the only PostgreSQL-supported store. Shared state, agent/session storage, and other stores continue using SQLite. Incognito storage remains memory-only. A supported store never silently falls back to SQLite when its PostgreSQL connection or admission fails.

Existing SQLite workboard data cannot switch engines yet. Keep that installation on SQLite until the future openclaw database port tool is available; selecting PostgreSQL refuses the existing store instead of copying, replacing, or ignoring its data. Omitting database preserves the SQLite default, so ordinary updates require no configuration or data migration.

Isolation and durability

One Gateway owns each store. PostgreSQL does not enable multiple active Gateways, external writers, replication management, or connection-pool management.

Writers use READ COMMITTED and a transaction-scoped advisory lock derived from the store identity. Snapshot reads use REPEATABLE READ READ ONLY. Connections set synchronous_commit=off to match SQLite WAL with synchronous=NORMAL: a server or operating-system crash can lose recently acknowledged commits. Backups and recovery must account for that durability policy. The adapter sets generous lock and statement timeouts once during connection setup; operations do not change timeout settings individually.

Anchors and backups

A PostgreSQL store keeps a small SQLite anchor at its usual local database path. The anchor records the store UUID, engine, and PostgreSQL schema. New schemas use <schemaPrefix>_<store>_<uuid-prefix>; moving the anchor preserves the identity. PostgreSQL schema metadata must match the anchor's store UUID and expected schema version. Admission checks run once per physical store per process load, and workers and handles reuse the admitted facts. Confirmed commits and uncertain commit outcomes invalidate cached store facts through the anchor's write token.

Back up both the local anchor and its PostgreSQL schema as a matched set. Stop the Gateway while taking or restoring that pair, and include the SQLite stores that still hold the rest of the installation. A local anchor backup alone does not contain PostgreSQL rows. See Backups for SQLite snapshot requirements; PostgreSQL backup and restore remain operator-managed.

Limits and recovery

Fresh stores bootstrap from generated PostgreSQL DDL at the current store schema version. PostgreSQL forward migrations are not available until schema lockstep tooling lands. A version mismatch refuses admission; restore a matching backup with its matching OpenClaw version, or keep the existing version until a supported migration exists. Never edit the schema version to bypass admission.

PostgreSQL text cannot contain NUL characters. Values containing NUL fail with an engine error. Search/vector capabilities remain store-specific; selecting an engine does not add PostgreSQL support to an unported store.

Failure Next step
Missing or unresolved connection secret Configure database.postgres.connection and verify the SecretRef provider is available to the Gateway.
Unreachable PostgreSQL server Check the DSN, network access, role permissions, and server availability.
Store UUID mismatch Restore the matching anchor and PostgreSQL schema; do not pair an anchor with another store.
Newer or otherwise mismatched schema Use the matching OpenClaw version or restore its matching backup; do not force a downgrade.

Observe database work

The engine boundary publishes Node.js diagnostics_channel events on openclaw.database.statement and openclaw.database.request. Subscriptions are isolate-local: statement events are emitted in the database worker, and request events on the host. Subscribe in each process or worker you want to measure:

js
import { channel } from "node:diagnostics_channel"; channel("openclaw.database.statement").subscribe((event) => {  // Count by event.engine, or inspect event.durationMs and event.outcome.});

Each connection execution call emits one statement event. Compound exec or query SQL produces one event carrying the normalized compound text; observation does not split SQL or execute extra queries. Each worker scope.execute request, connection open, and connection close emits one host-side request event. Both include engine ("sqlite" or "postgres"), threadId, outcome ("ok" or "error"), and durationMs. Statement events also include normalized sql with literals redacted; neither event includes bindings. Request events count the worker boundary separately from the statements that the request executes. This lets census tools compare both engines without inferring them from SQL. When no subscriber is present, the boundary skips timing, SQL normalization, and event construction.

See Database schemas for store ownership and schema contracts, and Configuration reference for the rest of the Gateway configuration.

Was this useful?