v0.2.0 · alpha Download Changelog GitHub
>_ docs / operations / Moving to Postgres
§ 04 Operations

Moving to Postgres

Grounded keeps your memory in one file on your computer. If that cabinet gets big, or several machines need to share it, you can move it into Postgres. This page walks through it one step at a time, and staying where you are is a perfectly good choice.

// Start here

Do you need this? #

Out of the box, Grounded stores everything in SQLite: a database that lives in a single file, with nothing extra to install or keep running. That is the right home for most people, and it does not stop working as you add more.

Postgres is a database that runs as its own program. It's worth the move when:

  • Your cabinet is large and you want a database built for big, busy workloads.
  • Several machines or agents share one cabinet and you want it on a server rather than one desktop.
  • You already run Postgres and would rather keep everything in one place.

For a sense of scale: a real cabinet of 792 sessions, 455 documents (6,047 chunks) and 48 facts fits in about 74 MB on Postgres, and a lexical recall round trip takes under 10 ms. If none of the reasons above fit you, stay on SQLite. Nothing about recall changes either way.

// In plain words

What pgvector is #

Grounded can search by meaning, not only by exact words. To do that, it turns each piece of text into an embedding: a list of numbers that works like a fingerprint of what the text means, so texts about the same idea get similar fingerprints. Plain Postgres can't store those fingerprints or compare them quickly. pgvector is an extension, an add-on you switch on inside Postgres, that teaches it to do exactly that. Grounded needs it: without pgvector, it can't use Postgres.

PieceWhat it does
PostgresThe database. Holds your facts, sessions, documents and vision.
pgvectorThe add-on. Lets Postgres store meaning fingerprints and find the closest ones fast.
GroundedYour memory service. Uses both, and works the same as it did on SQLite.
// The easy way

Get Postgres with pgvector #

The simplest route is Docker, a tool that runs ready-made programs in their own sealed box. Make a new folder, save this as docker-compose.yml inside it, and replace PASSWORD with a password of your own:

docker-compose.yml new folder
services:
db:
image: pgvector/pgvector:pg17
environment:
POSTGRES_USER: grounded
POSTGRES_PASSWORD: PASSWORD
POSTGRES_DB: grounded
ports:
- "127.0.0.1:5432:5432"
volumes:
- grounded-pg:/var/lib/postgresql/data
restart: unless-stopped
volumes:
grounded-pg:

Then, in that folder, start it:

start new folder
>docker compose up -d

That's the whole trick: the pgvector/pgvector image is Postgres with pgvector already inside, and Grounded switches it on by itself the first time it connects. Your data lives in a Docker volume named grounded-pg, so it survives restarts and updates. Postgres only answers on this computer; if Grounded runs on a different machine, remove 127.0.0.1: from the ports line.

Already have Postgres?

Install pgvector for your Postgres version, then create a database for Grounded. Grounded runs CREATE EXTENSION IF NOT EXISTS vector; on first connect. If its user isn't allowed to, have an administrator run CREATE EXTENSION vector; in that database once.

// One command

Move your cabinet #

Grounded finds Postgres through a connection URL: one line holding the user, password, address and database name. With the file above, it's the one below. Start with --dry-run, which only shows what would move and changes nothing:

preview ~
>grounded migrate-storage --to postgres --url postgres://grounded:PASSWORD@localhost:5432/grounded --dry-run

When the preview looks right, run it for real:

move ~
>grounded migrate-storage --to postgres --url postgres://grounded:PASSWORD@localhost:5432/grounded

Here's what it does, in order:

  1. Checks everything first, before it touches anything.
  2. Pauses Grounded so nothing changes mid-move.
  3. Copies everything — every fact, session, document and vision, with their embeddings.
  4. Checks the counts match between the old cabinet and the new one.
  5. Switches your config to Postgres and restarts Grounded.

Your SQLite file is left untouched as a backup. If any check fails, nothing is changed and Grounded keeps running on SQLite.

Needs the next Core release

migrate-storage arrives in the Grounded Core release after 0.2.0. On 0.2.0 you can wait for that update, or start fresh: point Grounded at an empty Postgres and restart. You can ingest your documents again, but facts, sessions and vision don't carry over.

To start fresh on 0.2.0, set these lines in ~/.grounded/config.toml, then run grounded restart:

config.toml ~/.grounded
[storage]
adapter = "postgres"
url = "postgres://grounded:PASSWORD@localhost:5432/grounded"
// Is it there?

Check it worked #

Open the console at http://127.0.0.1:7437. Its home page should show storage as postgres, with the same numbers of facts, sessions and documents you had before. From a terminal, the same answer comes from GET /health:

health ~
>curl -s localhost:7437/health
"storage": { "adapter": "postgres", "ok": true, … }
"counts": { "facts": 48, "sessions": 792, "documents": 455, … }
// Changed your mind

Undo #

Before switching, the move saves your old settings next to the new ones as config.toml.bak-<timestamp> in ~/.grounded. To go back, rename that file to config.toml (replacing the new one) and run grounded restart. You're back on SQLite, on the file that was never touched. Anything written while you were on Postgres stays in Postgres.

// Keep a copy

Backups on Postgres #

Your cabinet is no longer a single file you can copy, so take backups with Postgres's own tool, pg_dump. From the folder with your docker-compose.yml:

backup compose folder
>docker compose exec db pg_dump -U grounded grounded > grounded-backup.sql

That saves the whole grounded database, which holds only Grounded. To restore it into an empty database, run docker compose exec -T db psql -U grounded grounded < grounded-backup.sql. More on configuration in Self-hosting.