Skip to main content

The two tiers

Storage is an adapter, and the same schema and SQL run on both. Embedded is the right default: nothing to run, and backup is copying a folder. Move to server when you want concurrent database access, a store too big for one folder, or one shared across machines.

Backing up the embedded store

Stop first so the directory is quiescent. Restoring is the reverse. config.env travels separately.

Running on Docker Postgres

Start Postgres with pgvector

Point memloom at it

One line in ~/.memloom/config.env:

Start

memloom serve. The first init() creates the whole schema on an empty database, so there is no provisioning step. The CLI, MCP and viewer all follow the same config.
Two differences from embedded: the data-directory lock is unused, since Postgres handles concurrency, and the daemon does not start the wire bridge. Inspect the server directly.
pg is an optional dependency, so run pnpm add pg in the project that uses this.

Migrating existing data

Re-ingest is the simple path and loses history. Point the new store at the same embedding config and replay your sources. For a store that is mostly ingested documents this is usually enough, since documents are mirrors and re-ingesting reproduces them exactly. Copy the rows keeps everything. With the daemon running, pull through the wire and load into the target:
Create the target schema first by starting memloom serve once with MEMLOOM_PG_URL set, then load data-only.
The bridge is a single-connection path into PGLite. Fall back to per-table COPY ... TO STDOUT through psql, in dependency order:_memloom_meta, memory_objects, memory_schema, memory_entities, memory_edges, memory_dedup_decisions, context_documents, context_chunks, memory_index_runs, memory_index_events, assistant_sessions, assistant_messages.Skipping the later tables silently drops your schema vocabulary, indexing history and assistant chats.
The embedding fingerprint travels with the data. The engine you run against a migrated store must use the same embedding provider, model and dims, or init() refuses to open it. See Configuration.

What stays local

Whichever tier you run, the HTTP API binds to 127.0.0.1, CORS admits only localhost origins, and the only outbound traffic goes to the provider you configured. Self-hosting the database in Docker does not expose memloom to the network unless you publish the ports yourself.