MCP authentication mode: This local installation guide uses
METRONIX_MCP_API_KEYwithAUTH_ENABLED=false. If you enable hosted authentication withAUTH_ENABLED=true, clients must send a user JWT instead; the shared key is ignored.
This is the complete, by-hand installation guide for the Metronix Core backend. It takes you from an empty machine to a running stack you can verify with a health check.
Metronix runs as a Docker Compose stack. The canonical Compose file is
docker-compose.yml — use it for every command in this guide.
Once the backend is running, connect an AI agent to it with
[connecting_to_agent.md](connecting_to_agent.md).
Quick install from the web — the small bootstrap downloads the latest tagged release,
then runs the full installer from the managed checkout at
~/.metronix/metronix-memory:
curl -fsSL https://mtrnix.com/install.sh | bashTo inspect it first, use curl -fsSLo install.sh https://mtrnix.com/install.sh, review the
file, then run bash install.sh. Full-installer options go after --:
curl -fsSL https://mtrnix.com/install.sh | bash -s -- -- --mode memory -y
curl -fsSL https://mtrnix.com/install.sh | bash -s -- --update -- --admin -yThe web bootstrap is scripts/install-bootstrap.sh. Publish
that file—not the repository-root installer—because the full installer needs Compose,
templates, and documentation from the same checkout. Server configuration, atomic deploy,
verification, and release maintenance are documented in
docs/publishing-installer.md.
Quick install from a clone — ./install.sh checks Docker, writes .env, builds and
starts the stack, health-checks the API, and optionally wires an agent.
Common flags (see ./install.sh --help for the full list):
| Flag | Purpose |
|---|---|
-y, --yes |
Non-interactive; use defaults and flags, never prompt |
| `--mode memory | answers` |
--chat-url, --chat-model, --chat-api-key |
Chat LLM endpoint when --mode answers |
--openwebui |
Enable Open WebUI (:3080); only applies in answers mode |
--admin |
Install the Metronix Admin Console web UI (:3000, HTTPS); works in any mode |
--kb |
Deprecated alias for --admin (panel renamed to Metronix Admin Console) |
--connect-hermes |
Connect Hermes after install (or ./install.sh --connect-hermes -y alone) |
--connect-claude |
Connect Claude Code after install (or ./install.sh --connect-claude -y alone) |
--connect-codex |
Connect Codex after install (or ./install.sh --connect-codex -y alone) |
--connect-openclaw |
Connect OpenClaw after install (or ./install.sh --connect-openclaw -y alone) |
--agent-id, --metronix-url |
Override agent id / MCP URL written into the agent config |
--reconfigure |
Re-run .env setup even if .env already exists |
--fresh-docker-reset |
Delete Metronix containers, images, volumes, and build cache before reinstall |
./install.sh # memory store (default)
./install.sh --mode answers \
--chat-url https://api.deepseek.com/v1 --chat-model deepseek-chat \
--chat-api-key sk-... --openwebui -y # answers + Open WebUI, non-interactiveThis page is the by-hand reference — use it for full control or troubleshooting.
Run the repository-root uninstall script from the managed checkout or a clone:
./uninstall.sh # remove containers, including metronix-memory-frontend; keep data
./uninstall.sh --volumes --purge # permanently delete data and generated files; disconnect supported agentsSee uninstall.md for the exact cleanup scope and manual recovery steps.
The install is five steps:
- Check prerequisites
- Clone the repository
- Configure
.env— set the MCP key and Fernet key (+ optional chat LLM if using Open WebUI) - Launch the stack
- Verify
After that, see Ports, Common operations, and Troubleshooting for day-to-day reference.
- Docker Engine or Docker Desktop, with the daemon running.
- Docker Compose v2 (
docker compose) or the legacydocker-composebinary. - ~15 GB free disk space — images, build cache, volumes, and first-run Ollama model downloads.
- ≥6 GB RAM available to Docker (8 GB recommended), 4 CPUs. The full stack plus the
local graph model (
qwen2.5:3b, ~1.9 GB) does not fit in Docker Desktop's default ~2 GB — raise it under Settings → Resources → Memory, or a sync may be OOM-killed mid-run ("Sync interrupted (API restart)"). - Python 3.12+ — only if you intend to run tests or develop locally; not required to run the stack.
Verify Docker is installed and the daemon is up:
docker --version
docker compose version 2>/dev/null || docker-compose --version
docker info >/dev/null 2>&1 && echo "Docker is running successfully" || echo "DOCKER DAEMON IS NOT RUNNING! Start Docker via command: 'sudo systemctl start docker' or check prerequisites in install.md for more info"If Docker is missing, install it first:
- Linux: https://docs.docker.com/engine/install/
- macOS: https://docs.docker.com/desktop/setup/install/mac-install/
- Windows: https://docs.docker.com/desktop/setup/install/windows-install/
If the daemon is not running, start it: sudo systemctl start docker (Linux), or launch
Docker Desktop / OrbStack / colima start (macOS).
macOS note. Docker Desktop can lose ownership of
~/.dockerafter an update, which makesdocker compose buildfail withpermission denied. Fix it before step 4:sudo chown -R $(whoami):staff ~/.docker
git clone https://github.com/mtrnix/metronix-memory.git
cd metronix-memoryCreate your environment file from the template:
cp .env.example .envFor the usual path — agent memory over MCP (Hermes, Cursor, Claude Desktop, …) — you
need to set METRONIX_MCP_API_KEY and FERNET_KEY. Embeddings for ingest run on the
bundled Ollama container automatically (see §3e); you do
not need a chat LLM in .env.
| Scenario | What to set in .env |
|---|---|
| Agent memory (MCP) — default | METRONIX_MCP_API_KEY + FERNET_KEY |
| Open WebUI (§4) | MCP key + Fernet key + chat LLM (§3c) |
| Metronix generates answers | Same as Open WebUI — custom chat endpoint |
./install.shcopies.env.exampleand auto-generatesPOSTGRES_PASSWORD,NEO4J_PASSWORD,METRONIX_MCP_API_KEY,FERNET_KEY, andMETRONIX_SECRET_KEY. On a manual install the DB passwords ship blank in.env.example; Docker Compose falls back tometronix_devfor both Postgres and Neo4j unless you set them. Remove any emptyNEO4J_AUTH=line from.env— it breaks Neo4j startup (see Troubleshooting).
The MCP API key guards the MCP server endpoint (/mcp), which is how AI agents
(Hermes, Cursor, Claude Desktop, and other MCP clients) connect to Metronix. The key is a
token you choose — treat it like a password. You can generate a strong string using:
openssl rand -hex 32Set it in .env:
METRONIX_MCP_API_KEY=<paste-the-generated-token>Agents send this token as Authorization: Bearer <token> when connecting to
http://localhost:8000/mcp. The endpoint returns 401 without it.
MCP URL: http://localhost:8000/mcp is the default value for your host. It maps to the
metronix-full-api container (metronix-core in docker-compose.yml), port 8000,
path /mcp.
The default workspace id is pre-set to
MTRNIX(DEFAULT_WORKSPACE_IDin.env). You will need this value, your MCP key, and an agent UUID (configured in the agent runtime, not in this.env) when you connect an agent — see[connecting_to_agent.md](connecting_to_agent.md).
FERNET_KEY encrypts connector credentials at rest. The API boots fine without it, but
every call to metronix_source_create (or any other connector-add path) fails as soon as
it tries to encrypt a credential. The error code the MCP client sees is a generic
INTERNAL_ERROR either way — only the message field is descriptive:
metronix_source_create: FERNET_KEY not configured. Set the FERNET_KEY env var. Set it up
front even if you don't plan to add a source immediately.
Generate a key and set it in .env:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"FERNET_KEY=<paste-the-generated-key>Leave it blank only if you are certain you will never create a source — do not put a
non-Fernet placeholder in its place; an invalid key crashes credential encryption instead of
giving the clean "not configured" error. ./install.sh generates this automatically; a
manual install must set it by hand.
Configure this only if you will run Open WebUI (§4) or want Metronix itself to generate chat answers. For agent memory over MCP, skip this section — your external agent handles replies.
Set any OpenAI-compatible chat endpoint in .env:
LLM_PROVIDER=custom
LLM_PROVIDER_URL=https://your-llm-endpoint/v1
LLM_PROVIDER_API_KEY=your-key
LLM_PROVIDER_MODEL=deepseek-chat # model the endpoint serves (required)With ./install.sh, use --mode answers plus --chat-url, --chat-model, and optionally
--chat-api-key instead of editing by hand.
Neo4j requires a username/password. On a manual install with unchanged .env.example,
the defaults are:
- Username:
neo4j - Password:
metronix_dev
If you change NEO4J_PASSWORD in .env, use plain text only (not hashes). Neo4j does
not accept pre-hashed passwords. The install.sh script generates a random password
automatically; if you edit .env manually, ensure NEO4J_PASSWORD is set to a plain-text
value and leave NEO4J_AUTH unset (or do not edit it).
Docker Compose starts an Ollama container (metronix-full-ollama, host port 11435)
with the rest of the stack — no extra .env setup. On first launch its entrypoint runs
ollama pull nomic-embed-text. That embedding model is required for data ingest
(indexing documents, memory records, and connector content into Qdrant).
The entrypoint also pulls a small chat model — qwen2.5:3b (OLLAMA_LLM_MODEL) — used
by default for knowledge-graph extraction (entity/relationship NER during ingest) and
local answer generation, so a default install builds the graph with no external LLM. This
is separate from the external answer-generation endpoint in
§3c. The first docker compose up
may take extra time while both models download.
Inside the Docker network the service is ollama:11434.
Build and start the stack. The first run builds images from source and pulls the Ollama
embedding model (nomic-embed-text) plus the small graph model (qwen2.5:3b),
which takes about 10–15 minutes. Subsequent runs are fast.
Backend only — PostgreSQL, Qdrant, Neo4j, Redis, Ollama (for embeddings), SPLADE, embedding proxy, and the Metronix API:
docker compose up -d --buildBackend + Open WebUI — adds a browser chat interface at http://localhost:3080.
Configure a chat LLM first (§3c);
Open WebUI calls Metronix's OpenAI-compatible API for answers and is useless without one.
docker compose --profile openwebui up -d --buildOpen WebUI requires no login and connects to Metronix automatically via the pre-configured
OPENAI_API_BASE_URL.
./install.shenables Open WebUI only in--mode answers. In memory mode,--openwebuiis ignored with a warning.
Backend + Metronix Admin Console — adds the open-source web admin UI at
https://localhost:3000 (connect data sources and chat-bot channels, upload files, monitor
service/database health). Unlike Open WebUI, it works in any mode — it talks to the REST
API, not a chat model. The console is served over HTTPS by Caddy using its internal
CA (self-signed certificate) by default — see frontend/Caddyfile to
switch to automatic Let's Encrypt for a public domain.
docker compose --profile admin up -d --buildOverride the published port with ADMIN_FRONTEND_PORT (default 3000). The legacy
KB_FRONTEND_PORT is still honored as a fallback for one release. See
[frontend/README.md](frontend/README.md) for details. ./install.sh offers this as the
"Install the Metronix Admin Console" prompt, or non-interactively via --admin (the
--kb alias still works but prints a deprecation warning).
Check that every service is up and the API is healthy:
docker compose ps
curl http://localhost:8000/healthA healthy backend exposes:
| Surface | URL |
|---|---|
| API health | http://localhost:8000/health |
| REST API | http://localhost:8000/api/v1/* |
| MCP endpoint | http://localhost:8000/mcp — metronix-full-api container, path /mcp (from Docker network: http://metronix-core:8000/mcp) |
| OpenAI-compatible API | http://localhost:8000/v1 |
Metronix Admin Console (with --profile admin) |
https://localhost:3000 (HTTPS, self-signed by default) |
Open WebUI (with --profile openwebui) |
http://localhost:3080 |
Next step: connect an agent over MCP — see
[connecting_to_agent.md](connecting_to_agent.md).
Optional: run the LongMemEval-S agent-memory benchmark — see
[docs/benchmarks/longmemeval.md](docs/benchmarks/longmemeval.md). Configure the benchmark in
benchmarks/longmemeval/.env.benchmark (not the repo-root .env).
If .env or containers already exist, re-running ./install.sh inspects the deployment
and offers a menu instead of blindly overwriting config:
| Action | When |
|---|---|
Fix .env and restart |
Blank secrets or empty NEO4J_AUTH= |
| Rebuild stack | Containers exist but API is down |
Reset volumes (down -v) |
Unhealthy Neo4j, or a Postgres/Neo4j password mismatch on an old volume |
| Fresh Docker reset | --fresh-docker-reset — removes images, volumes, build cache |
| Reconfigure | --reconfigure — rewrite .env from scratch |
After a successful install the script may wire an agent — pick Hermes, Claude Code, Codex, or OpenClaw interactively, or force one with a flag:
- Interactive prompt: choose Hermes (edit
~/.hermes/config.yaml+SOUL.md), Claude Code (claude mcp add, or edit~/.claude.jsonif the CLI is missing), Codex (edits~/.codex/config.tomldirectly —codex mcp addcan't set the requiredX-Agent-Idheader), or OpenClaw (openclaw mcp set+~/.openclaw/workspace/SOUL.md), or write a paste-ready guide for any other client. ./install.sh --connect-hermes -y— apply Hermes MCP wiring without prompting (requires existing.env)../install.sh --connect-claude -y— apply Claude Code MCP wiring without prompting, at user scope by default (requires existing.env)../install.sh --connect-codex -y— apply Codex MCP wiring without prompting, at user scope by default (requires existing.env)../install.sh --connect-openclaw -y— apply OpenClaw MCP wiring without prompting (requires existing.env).- Either way, filled prompts land in a per-runtime directory (gitignored):
metronix-hermes-setup/,metronix-claude-code-setup/, andmetronix-codex-setup/each hold1-install-mcp.md,2-memory-source.md,3-migrate.md;metronix-openclaw-setup/(andmetronix-agent-setup/for any other client) holds a single filledprompts.mdwith the same prompts inside. Paste prompts 2 and 3 after restarting the agent — see[docs/integrations/hermes-agent.md](docs/integrations/hermes-agent.md),[docs/integrations/claude-code.md](docs/integrations/claude-code.md),[docs/integrations/codex.md](docs/integrations/codex.md), or[docs/integrations/openclaw.md](docs/integrations/openclaw.md).
Manual install: use [connecting_to_agent.md](connecting_to_agent.md) instead of the script
for agent setup.
| Service | Host port |
|---|---|
| API | 8000 |
| PostgreSQL | 5433 |
| Qdrant HTTP | 6335 |
| Qdrant gRPC | 6336 |
| Neo4j HTTP | 7475 |
| Neo4j bolt | 7688 |
| Redis | 6380 |
| SPLADE | 8080 |
| Embedding proxy | 8002 |
| Ollama | 11435 |
| Metronix Admin Console | 3000 |
| Open WebUI | 3080 |
View API logs:
docker compose logs metronix-coreRestart the API:
docker compose restart metronix-coreRebuild after editing .env or source:
docker compose up -d --build --force-recreateStop the stack:
docker compose downStop the stack and delete all data volumes:
docker compose down -v- Linux:
sudo systemctl start docker - macOS / Windows: start Docker Desktop. On macOS, OrbStack or
colima startalso work.
sudo usermod -aG docker $USER
newgrp dockerOr prefix Docker commands with sudo.
Docker Desktop can lose ownership of ~/.docker after an update:
sudo chown -R $(whoami):staff ~/.dockerStop any previous Metronix run, then find what occupies the port:
docker compose down
sudo lsof -i :8000 # Linux / macOSOn Windows PowerShell:
netstat -ano | findstr :8000The agent must send the configured key:
Authorization: Bearer <METRONIX_MCP_API_KEY>
The token must exactly match METRONIX_MCP_API_KEY in the server .env.
Confirm the API is healthy first, then inspect Open WebUI logs:
curl http://localhost:8000/health
docker compose logs open-webuiIf Neo4j fails to start with the error dependency failed to start: container metronix-full-neo4j is unhealthy, check the logs:
docker logs metronix-full-neo4jA common cause is an invalid NEO4J_AUTH value in .env. If NEO4J_AUTH is set to an
empty string, Neo4j receives a blank username and rejects it. Fix:
# Remove the NEO4J_AUTH= line from .env
sed -i '/^NEO4J_AUTH=$/d' .env
# Restart the stack
docker compose down
docker compose up -dAlso ensure NEO4J_PASSWORD is a plain text password, not a hash. Neo4j does not accept
pre-hashed passwords.
Neo4j only sets the initial password on first startup. If you change NEO4J_PASSWORD
in .env on a system that already has the Neo4j data volume, the database keeps the old
password and the healthcheck (which reads the new one) will fail.
To reset the database with the new password:
docker compose down -v
docker compose up -dWarning:
down -vdeletes ALL data volumes (PostgreSQL, Qdrant, Neo4j, Redis, Ollama). This is a full reset — only do it if you're starting fresh.
To stop a reinstall from silently producing a broken stack, install.sh checks — before
generating any secrets — whether a Neo4j or Postgres data volume from a previous install
still exists while .env has no usable password for it (the file was deleted, or
NEO4J_PASSWORD / POSTGRES_PASSWORD is blank). Because both databases fix their password on
first startup and never change it on an existing volume, a freshly generated random
password is guaranteed to be rejected. Rather than launch a stack that fails authentication,
the installer stops and asks you to choose:
-
Keep the data — put the original
NEO4J_PASSWORD/POSTGRES_PASSWORDback in.env, then rerun./install.sh -y. -
Discard the data (DESTROYS it):
docker compose -f docker-compose.yml down -v && ./install.sh -y --reconfigure
The DB password lives in two places: the Docker data volume (fixed on first start) and
your .env. Clearing only one leaves the other in charge:
- A plain
docker compose down(without-v),docker system prune(without--volumes), ordocker rmdoes not remove the named data volumes — the old password survives in the volume. .envalso persists the password.install.shregenerates a DB password only when that database's data volume is gone — on a normal--reconfigurewith the volume still present it deliberately reuses the existingNEO4J_PASSWORD/POSTGRES_PASSWORD(it never rotates a live database's password). So the trigger for a new password is the volume, not the.envvalue: wiping the volume is what lets a new one take effect.
A full reset wipes the volumes, so the DB passwords are regenerated on the next run (other secrets like the MCP key are preserved):
./install.sh -y --fresh-docker-resetTo force new DB passwords by hand, just remove the volumes (the installer regenerates any DB password whose volume no longer exists) and reinstall:
docker compose -f docker-compose.yml down -v
./install.sh -y --reconfigureLike Neo4j, Postgres fixes its password on first startup and keeps it in its data
volume. If POSTGRES_PASSWORD in .env later differs from that value, Postgres still
starts (its healthcheck does not authenticate) but rejects every query with
password authentication failed for user "metronix" — so the stack can look healthy while
memory writes fail. This usually happens when .env is deleted or regenerated without
also resetting the volume.
docker logs metronix-full-postgres | grep "password authentication failed"Restore the original POSTGRES_PASSWORD in .env, or reset the data (full wipe):
docker compose down -v
docker compose up -d --buildWarning:
down -vdeletes ALL data volumes. Only do this when starting fresh.