> ## Documentation Index
> Fetch the complete documentation index at: https://docs.memloom.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Self-hosting & migration

> From a folder on your laptop to a Postgres server in Docker

## The tiers

memloom's storage is an adapter, and the same schema and SQL run on both implementations:

| Tier                   | Storage                                                              | Who owns the connection                        | How to select                    |
| ---------------------- | -------------------------------------------------------------------- | ---------------------------------------------- | -------------------------------- |
| **Embedded** (default) | PGLite (Postgres-in-a-folder at `~/.memloom/data`)                   | The daemon, exclusively                        | Nothing to set                   |
| **Server**             | Real Postgres (Docker, Supabase, any managed Postgres with pgvector) | The Postgres server (pooled, many connections) | `MEMLOOM_PG_URL` in `config.env` |

The embedded tier is the right default: there is nothing to set up or run, and backup is
copying a folder.
Reach for the server tier when you want concurrent DB access (the PGLite single-connection
lock is the thing you feel), a store bigger than comfortable in one folder, or a store shared
across machines.

## Backing up the embedded store

The whole store lives in the data folder. Stop the daemon first so the data directory is
quiescent:

```bash theme={null}
memloom stop
cp -r ~/.memloom/data ~/backups/memloom-2026-07-12
```

Restoring is the reverse. `config.env` travels separately (same folder, one file).

## Running against Docker Postgres

**1. Start Postgres with pgvector:**

```yaml theme={null}
# docker-compose.yml
services:
  db:
    image: pgvector/pgvector:pg17
    environment:
      POSTGRES_PASSWORD: memloom
    ports:
      - "5432:5432"
    volumes:
      - memloom-data:/var/lib/postgresql/data
volumes:
  memloom-data:
```

**2. Point memloom at it.** One line in `~/.memloom/config.env`:

```bash theme={null}
MEMLOOM_PG_URL=postgresql://postgres:memloom@localhost:5432/postgres
```

Then `memloom serve`. The daemon connects with a pooled `pg` adapter instead of opening the
PGLite folder, and the first `init()` creates the full schema on an empty database, so there
is no separate provisioning step. Everything else (CLI, MCP, viewer, `memloom reembed`) reads
the same config and follows. Two differences from the embedded tier: the data-dir lock is not
used (the Postgres server handles concurrency), and the daemon does not start the wire-protocol
bridge on 54329 (inspect the server directly with any Postgres client).

**Embedding memloom as a library instead?** The same tier is one constructor argument:

```ts theme={null}
import { Memloom, PgAdapter } from "@memloom/core";

const storage = await PgAdapter.connect(
  "postgresql://postgres:memloom@localhost:5432/postgres",
);
const memloom = new Memloom({ storage, embedding, llm });
await memloom.init();   // runs migrations, stamps/checks the embedding fingerprint
```

`pg` is an optional dependency: run `pnpm add pg` in the project that runs this.

## Migrating existing data

Two paths, depending on how much history you care about:

**Re-ingest (simple, loses history).** Point the new store at the same embedding config and
replay your sources: `context add` your files again, re-save what matters. Version chains and
resolved conflicts don't come along. For a store that's mostly ingested documents, this is
usually all you need: documents are mirrors, and re-ingesting reproduces them exactly.

**Copy the rows (full fidelity).** The daemon speaks the Postgres wire protocol on
`127.0.0.1:54329`, so with the daemon running you can pull data out with standard tools and
load it into the Docker database:

```bash theme={null}
pg_dump -h 127.0.0.1 -p 54329 -U postgres -d postgres --data-only \
  > memloom-dump.sql
psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -f memloom-dump.sql
```

Run the target's schema first (start `memloom serve` once with `MEMLOOM_PG_URL` pointed at
the Docker database; it creates the schema), then load data-only. The wire server is a single-connection bridge into PGLite. If `pg_dump`
trips over it, fall back to per-table `COPY ... TO STDOUT` via `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 ones
silently drops your schema vocabulary, indexing history, and assistant chats.

<Warning>
  The embedding fingerprint travels with the data (it's a row in `_memloom_meta`). The engine
  you run against the migrated store must be configured with the **same embedding provider,
  model, and dims**, or `init()` will refuse to open it (by design). See
  [Configuration](/guides/configuration#the-embedding-fingerprint).
</Warning>

## What stays local no matter what

Whichever tier you run, the trust model doesn't change: the HTTP API binds to `127.0.0.1`,
CORS admits only localhost origins, and the only outbound traffic is to the embedding/LLM
provider you configured. Self-hosting the database in Docker doesn't expose memloom to the
network unless you publish the ports yourself.
