RememberStackremember.dev/docs

Requirements

A self-hosted RememberStack is a set of containers from one Compose file: PostgreSQL, SeaweedFS object storage, an API process and twelve pipeline workers. The Compose file builds or pulls everything it needs. This page lists what has to be true of the machine and the accounts around it.

Docker and Compose

You need Docker Engine with the Compose v2 plugin: the docker compose command, not the older docker-compose script. compose.yaml uses a top-level name, depends_on conditions such as service_completed_successfully, profiles, and an optional env_file (required: false), which needs Compose 2.24.0 or later.

Run the tokenless quickstart only on Docker Engine 28.0.0 or later (docker version --format '{{.Server.Version}}'). Compose publishes the API on 127.0.0.1 only, and before Engine 28.0.0 a port published on 127.0.0.1 can still be reached by other hosts on the same local network. On an older engine, turn on API authentication before the first start: set REMEMBERSTACK_SELFHOST_API_BEARER_TOKEN and REMEMBERSTACK_SELFHOST_REQUIRE_API_AUTH=true in .env (see A shared secret).

The project states no other minimum Docker or Compose version. Its own continuous-integration check runs docker compose up --build --detach --wait on a current GitHub Actions Ubuntu runner.

Image architectures

Two images from this project make up the stack. Both are published for linux/amd64 and linux/arm64, so an arm64 machine (Apple Silicon, Graviton, Ampere) runs them natively, without emulation:

ImageUsed by
ghcr.io/writeitai/remember-stack:0.17.2The API, the workers and setup
ghcr.io/writeitai/remember-stack-postgres:19beta3-0.17.2PostgreSQL

Compose pulls both from GitHub Container Registry. It builds an image from source (Dockerfile or Dockerfile.postgres) only when the pull fails, or when you pass --build. Building PostgreSQL compiles one extension and takes a few minutes. The third image, SeaweedFS, is also published for both architectures.

PostgreSQL 19 (beta)

RememberStack requires PostgreSQL 19, which is a beta release. The Compose file pins postgres:19beta3.

It needs 19 because the memory's graph is a SQL/PGQ property graph (CREATE PROPERTY GRAPH, queried with GRAPH_TABLE). PostgreSQL 19 is the first release with SQL/PGQ, so entity neighbourhoods and paths run live inside the same database that holds the facts, with no separate graph store to build or keep in sync.

Running a beta has a consequence you should plan for: there is no promise that a data directory created by one PostgreSQL 19 prerelease opens under a later one. See Upgrades and migrations.

Extensions

The migrations create these extensions:

ExtensionVersionUsed for
vector (pgvector)0.8.6Semantic search vectors and HNSW indexes
pg_textsearch1.3.1BM25 keyword search
pg_partman5.5.0Monthly partitioning of large tables
pgcryptocontribUUID generation and digests
pg_trgmcontribFuzzy name matching during entity resolution
fuzzystrmatchcontribPhonetic name matching (daitch_mokotoff)
unaccentcontribAccent folding of names
btree_gistcontribThe time-range exclusion constraint on relations

pg_textsearch has no PostgreSQL 19 package yet. Dockerfile.postgres downloads a pinned source revision from timescale/pg_textsearch, applies a small compatibility patch (docker/pg_textsearch-pg19.patch) and compiles it. pgvector and pg_partman come from the PostgreSQL apt archive at the exact versions above. remember ops graph-catalog ensure checks these three versions (see Operating the pipeline).

Server settings

Compose starts PostgreSQL with:

shared_preload_libraries=pg_textsearch,pg_partman_bgw
pg_partman_bgw.dbname=<REMEMBERSTACK_POSTGRES_DB>
pg_partman_bgw.role=<REMEMBERSTACK_POSTGRES_USER>
max_connections=<REMEMBERSTACK_POSTGRES_MAX_CONNECTIONS, default 300>

Everything else stays at the image defaults.

Running the engine against a PostgreSQL server you manage yourself is not a documented path. If you try it, the server must match everything on this page: version 19, the extensions at those versions, and the preload settings.

S3-compatible object storage

Original files, converted Markdown and other artifacts, and filesystem snapshots live in object storage, in three buckets (remember-raw, remember-artifacts, remember-corpusfs). The setup step creates them.

Compose runs SeaweedFS for this (image chrislusf/seaweedfs, pinned to 4.44 by digest). The engine talks to it through the S3 API with path-style addressing and SigV4 signing. It writes every object with a conditional create (If-None-Match: *), so an object is never silently replaced. Only SeaweedFS is exercised by the project's checks. Another S3-compatible store has to support path-style requests, conditional writes and user metadata on objects.

SeaweedFS stores objects in volumes of at most 1 GiB. Compose allows 100 of them, so the bundled store holds up to about 100 GiB before writes fail (each bucket also reserves 7 volumes on its first write). Volumes are not preallocated, so unused capacity takes no disk. To store more, raise -volume.max on the object-store service in compose.yaml. The engine sets no S3 lifecycle (expiry) rules and does not need them: it deletes objects itself, during a hard forget.

Model provider keys

KeyRequiredUsed for
OpenRouter API key (REMEMBERSTACK_OPENROUTER_API_KEY)YesEvery model call: structure, claim extraction, entity resolution, fact adjudication, and all embeddings, including query embeddings for search
Mistral API key (REMEMBERSTACK_MISTRAL_OCR_API_KEY)Only if you route PDFs or images to OCRThe mistral_ocr and image_ocr_description converters
A second OpenRouter key (REMEMBERSTACK_IMAGE_DESCRIPTION_API_KEY)Only if you route images to image_ocr_descriptionThe vision-model description of an image
TypeSafe AI key (REMEMBERSTACK_TYPESAFE_API_KEY)Only with REMEMBERSTACK_FACT_ADJUDICATION_ENGINE=jevThe alternative fact adjudicator

The engine starts with the placeholder OpenRouter key from .env.example, but the first document or search fails at the first model call. The model calls cost money on your OpenRouter account; see Models and providers.

Sizing

No hardware minimum has been measured, and the project publishes no throughput figures. What follows is what the configuration and schema fix, so you can size a machine from your own corpus.

Where it has run. Continuous integration starts the full stack on a standard GitHub-hosted Ubuntu runner, which for a public repository has 4 CPUs and 16 GB of memory. It ingests a document and waits for every stage there; it does not measure speed.

What uses CPU and memory. Most pipeline work is waiting on model calls to OpenRouter, so the engine's own processes are light. PostgreSQL does the local heavy lifting: search, graph queries and SQL queries all run in it. The query-side memory each of those may take is bounded (see the table below and Scaling).

What uses disk. Two volumes grow with the corpus:

  • postgres-data holds every chunk, claim, fact, observation and entity. Each of these also carries a 1,536-dimension embedding, about 6 KB, plus its share of the vector index.
  • object-store-data holds each uploaded file as sent, its converted Markdown and other artifacts, and filesystem snapshots. The bundled store is capped at about 100 GiB (see S3-compatible object storage).

Nothing is deleted automatically; a hard forget removes a document's data.

Database connections. Compose starts PostgreSQL with max_connections from REMEMBERSTACK_POSTGRES_MAX_CONNECTIONS, 300 by default (the image default is 100). Each engine process opens connections as it needs them, up to these ceilings:

ProcessGeneral poolOtherCeiling
api15 (5 kept open, 10 more under load)retrieval pool 4, graph pool 423
Each worker-…15 (5 kept open, 10 more under load)1 held open to listen for new work16

The ceilings of the default stack add up to 23 + 12 × 16 = 215, which fits within 300 with room to spare. They are ceilings, not what a stack holds: pools open connections only on demand. Nothing caps the total, though, so budget against the ceilings when you add replicas or API processes: each worker replica can add 16 connections and each API process 23, plus any increase in the retrieval and graph pool sizes. When the sum passes the limit, raise it by setting REMEMBERSTACK_POSTGRES_MAX_CONNECTIONS in .env; see Scaling.

Other configured bounds:

BoundDefault
Containers started by docker compose up15: postgres, object-store, setup (exits), api, 12 workers
Items one worker process runs at once1
Claims per second per worker process20, burst 20
Graph connection pool in the API4 connections, at most 2 graph queries at once
Graph query work_mem16,384 KiB
Retrieval connection pool in the API4 connections, at most 4 retrievals at once
SQL query sandbox5 s default and 15 s maximum statement time, 16 MiB work_mem, 8 statements at once per deployment
PostgreSQL max_connections300 (REMEMBERSTACK_POSTGRES_MAX_CONNECTIONS)

Scaling explains each bound and how to change it.