-
Notifications
You must be signed in to change notification settings - Fork 0
Installation
Contextator ships as one Docker image containing PostgreSQL 16 with pgvector and the Node.js application. There is no separate database to install and no migration step: the schema is created and kept up to date on every start.
- Requirements
- Docker Compose with the published image (recommended)
- Plain
docker run - Build the image yourself
- The first account
- Behind a reverse proxy
- From source, without Docker
- Upgrading
- Uninstalling
| Docker | Any recent version with Compose v2 (docker compose, not docker-compose) |
| Disk | ~1 GB for the image, ~470 MB for the default embedding model (~120 MB with EMBEDDING_DTYPE=q8), plus your documents and their vectors |
| RAM | 2 GB is comfortable; embedding is CPU-bound, not memory-hungry |
| CPU | Any x86-64 or ARM64 CPU. No GPU is needed or used. The published image is built for linux/amd64 and linux/arm64, so it runs on Apple Silicon without emulation |
| Network | Only for the one-time model download and for remote sources (git, Notion). Air-gapped installs are possible — see Embedding Models |
Runs on Linux, macOS and Docker Desktop for Windows (WSL 2 backend).
No clone needed — just the compose file and the annotated environment template:
mkdir contextator && cd contextator
curl -fsSLO https://raw.githubusercontent.com/Contextator/Contextator/main/docker-compose.yml
curl -fsSLO https://raw.githubusercontent.com/Contextator/Contextator/main/.env.example
cp .env.example .envEdit .env — at minimum, point it at your documentation:
DOCS_HOST_PATH=/path/to/your/docs # mounted read-only at /docs in the container
PORT=3444 # host port
SETUP_CODE= # optional: pick the first-run code yourselfThen:
docker compose up -d
docker compose logs -f # wait for "embedding model ready"Open http://localhost:3444/ and create the first account — see The first account.
- Pulls and runs one container named
contextator, publishing${PORT}:3444. The published image iscontextator/contextator. - Creates three volumes that survive
docker compose down, upgrades and container removal:contextator-pgdata(the database),contextator-models(downloaded models) andcontextator-data(uploaded files, git checkouts, Notion pulls). - Mounts
DOCS_HOST_PATHread-only at/docs. - Restarts the container unless you stopped it (
restart: unless-stopped).
Inside the container, PostgreSQL listens only on 127.0.0.1 and is not published — see
Backup and Data for how to reach it when you need to.
Without Compose, mount the same four paths yourself:
docker run -d --name contextator -p 3444:3444 \
-e SETUP_CODE=whatever-you-like \
-v contextator-pgdata:/var/lib/postgresql/data \
-v contextator-models:/app/.cache/models \
-v contextator-data:/data \
-v /path/to/your/docs:/docs:ro \
contextator/contextatorAdd any other settings with -e — see Configuration.
The compose file above pulls contextator/contextator. To build it from source instead — to try a
change to the Dockerfile, or on an architecture the published image does not cover — clone the
repository and layer the build override on top:
git clone https://github.com/Contextator/Contextator.git contextator && cd contextator
cp .env.example .env
docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --builddocker-compose.build.yml builds the image from the checked-out source and tags it
contextator/contextator:source — deliberately not latest. Compose's default pull_policy is
missing, so a shared tag would make the two workflows clobber each other silently: build once, and a
later plain docker compose up -d would keep running your old local build under the published name
instead of pulling the real one. Everything else — volumes, environment, ports — is exactly what
docker-compose.yml defines.
The dashboard requires an account, and a fresh installation has none. Until the first one exists, every start prints a one-time setup code on stdout:
┌─ Contextator first-run setup ───────────────────────────────────────────┐
│ No user accounts exist yet; the dashboard is waiting for its first one. │
│ │
│ Open http://localhost:3444/setup │
│ Code KRTW-9MHD-2PQF │
│ │
│ A new code is printed on every start until that first account exists. │
└─────────────────────────────────────────────────────────────────────────┘
docker compose logs -f shows it. Open /setup — / redirects there by itself — and give the code, a
username and a password of at least 12 characters. That account is root, and creating it closes setup
for good; there is nothing left to rotate.
Set SETUP_CODE in .env to choose the code instead, the same way you would choose
POSTGRES_PASSWORD. A code you chose is not echoed into the log. If a generated one is lost, restart
the server and read the new one.
Everything after that — more accounts, roles, per-project members — is on Accounts and Permissions.
Serving the dashboard over plain HTTP on a LAN address? Set
AUTH_COOKIE_SECURE=0, or browsers drop the session cookie and sign-in loops back to/login.
A project's MCP endpoint is open unless you require a token on it (Security). If the server is reachable by anyone you do not trust, close the projects that should not be public and terminate authentication in front of the rest.
Three things to get right:
- Do not buffer responses. The legacy SSE transport is a long-lived stream.
- Tell the dashboard its public URL so the snippets it prints are correct.
-
Pin the cookie flag. The proxy terminates TLS, so the application only learns the scheme from
X-Forwarded-Proto. Say it outright instead:
PUBLIC_BASE_URL=https://docs.example.com
AUTH_COOKIE_SECURE=1An nginx location that works:
location / {
proxy_pass http://127.0.0.1:3444;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
proxy_buffering off;
proxy_read_timeout 1h;
}If browsers on another origin must reach /mcp/*, list that origin in ALLOWED_ORIGINS. Command-line
clients send no Origin header and are always allowed.
Useful for development or when you already run PostgreSQL.
docker compose -f docker-compose.dev.yml up -d # PostgreSQL 16 + pgvector on localhost:5432
cp .env.example .envIn .env:
DATABASE_URL=postgres://contextator:contextator@localhost:5432/contextator
ALLOWED_DOC_ROOTS=/home/me/docs # or C:/Users/me/docs on Windows
DATA_DIR=.data # git checkouts, uploads and Notion pulls
SECRET_KEY= # only needed for private repositories / NotionThen:
npm install
npm run dev # tsx watch → http://localhost:3444
npm test # unit tests
npm run typecheckRequires Node.js 22 or newer — the floor in package.json's engines, and the version the container ships — and a PostgreSQL 16 with the vector extension.
docker compose pull
docker compose up -dBuilding from source instead: git pull, then
docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build.
The schema is brought up to date automatically at startup. Your data is in volumes and is untouched by an upgrade. The only upgrade that needs a decision from you is a change of embedding dimensions — see Embedding Models.
Tags. latest always points at the newest stable release; 0.1 tracks the latest patch inside the
0.1.x line; 0.1.0 is one exact, immutable release. Pin a versioned tag for anything you upgrade
deliberately by setting CONTEXTATOR_TAG in .env (e.g. CONTEXTATOR_TAG=0.1.0) — this is read at
every start, not only the first.
docker compose down # stops and removes the container — DATA IS KEPT
docker compose down -v # also deletes the volumes — DATA IS GONEYour original documentation is never modified: /docs is mounted read-only, and local directory
sources are scanned in place rather than copied.
Contextator · AGPL-3.0-or-later · Self-hosted MCP documentation server
Getting started
Your documentation
Using it
Operations