Skip to content

Installation

Muhammet Şafak edited this page Sep 19, 2026 · 4 revisions

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 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).


Docker Compose with the published image (recommended)

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 .env

Edit .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 yourself

Then:

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.

What the compose file does

  • Pulls and runs one container named contextator, publishing ${PORT}:3444. The published image is contextator/contextator.
  • Creates three volumes that survive docker compose down, upgrades and container removal: contextator-pgdata (the database), contextator-models (downloaded models) and contextator-data (uploaded files, git checkouts, Notion pulls).
  • Mounts DOCS_HOST_PATH read-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.


Plain docker run

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/contextator

Add any other settings with -e — see Configuration.


Build the image yourself

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 --build

docker-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 first account

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.


Behind a reverse proxy

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:

  1. Do not buffer responses. The legacy SSE transport is a long-lived stream.
  2. Tell the dashboard its public URL so the snippets it prints are correct.
  3. 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=1

An 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.


From source, without Docker

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 .env

In .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 / Notion

Then:

npm install
npm run dev        # tsx watch → http://localhost:3444
npm test           # unit tests
npm run typecheck

Requires 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.


Upgrading

docker compose pull
docker compose up -d

Building 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.


Uninstalling

docker compose down            # stops and removes the container — DATA IS KEPT
docker compose down -v         # also deletes the volumes — DATA IS GONE

Your original documentation is never modified: /docs is mounted read-only, and local directory sources are scanned in place rather than copied.

Clone this wiki locally