Ctrl K
ComunidadGuías…

docs/docker.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
<!-- authority:non-specification -->

# exomem — Docker

The fastest way to try exomem with Claude Code: no `uv`, no Python, no clone.

```bash
claude mcp add exomem -- docker run -i --rm -v "/path/to/vault:/vault" -e EXOMEM_VAULT_PATH=/vault ghcr.io/artexis10/exomem:latest --transport stdio
```

Replace `/path/to/vault` with your Obsidian/markdown vault's absolute path.
Claude Code launches the container per session and talks MCP over stdio — no
HTTP port, no OAuth, nothing to keep running in the background.

For an always-on remote deployment (mobile/web access via claude.ai), skip to
[Remote compose deployment](#remote-compose-deployment) below. See
[deployment.md](deployment.md) for the architecture this container fits into
— this doc is the Docker-specific path through it.

## Tags

| Tag | Variant | Search mode | Uncompressed size (approx.) |
| --- | --- | --- | --- |
| `latest`, `X.Y.Z` | `lean` | keyword/BM25 only, no torch | ~250–350 MB |
| `ml`, `X.Y.Z-ml` | `ml` | hybrid embeddings + CLIP search, CPU-only torch | ~1.5–2 GB |
| `cuda`, `X.Y.Z-cuda` | `cuda` | hybrid embeddings + CLIP search, CUDA-capable torch, CPU-default at idle | larger; NVIDIA/Linux amd64 only |

`lean` is the default and is what the stdio one-liner above pulls — no torch,
no model download, matching a fast zero-Python onboarding path. Opt into
hybrid search with the `ml` tag (`ghcr.io/artexis10/exomem:ml`); the first
`find` call downloads the embedding/CLIP model weights into the `/data` volume
(see below), so later restarts don't re-download them.

For NVIDIA Linux hosts, use the CUDA-capable image:

```bash
docker run --gpus all -i --rm \
  -v "/path/to/vault:/vault" \
  -v exomem-data:/data \
  -e EXOMEM_VAULT_PATH=/vault \
  ghcr.io/artexis10/exomem:cuda --transport stdio
```

The CUDA image is **capable**, not **resident by default**. It still boots in
normal mode, where exomem's steady-state torch models select CPU so the container
does not grab VRAM at idle. Opt into GPU use explicitly when you want it:

```bash
docker run --gpus all -i --rm \
  -v "/path/to/vault:/vault" \
  -v exomem-data:/data \
  -e EXOMEM_VAULT_PATH=/vault \
  -e EXOMEM_MODE=performance \
  ghcr.io/artexis10/exomem:cuda --transport stdio
```

Use `ml` for reproducible hybrid search without NVIDIA runtime. Use native install
instead ([../QUICKSTART.md](../QUICKSTART.md), [deployment.md](deployment.md)'s
CUDA/GPU notes) when you need Windows live-vault watcher reliability or macOS
Apple Silicon MPS/MLX.

## Volume contract

- **`/vault`** — your notes. Bind-mount your vault root here
  (`-v "/path/to/vault:/vault"`) and set `EXOMEM_VAULT_PATH=/vault`. All file
  reads and writes stay confined to this mount, matching the server's
  existing vault-containment guarantee.
- **`/data`** — server-local state, declared as a `VOLUME` in the image:
  - `EXOMEM_LOG_DIR=/data/logs` — the rotating `exomem.log`. The
    `queries.jsonl` / `writes.jsonl` / `reads.jsonl` audit logs land here too,
    but only in the `ml` image (the lean image runs with embeddings disabled,
    which also turns the query log off).
  - `ml`/`cuda` only: `HF_HOME=/data/hf` — downloaded embedding/CLIP model weights.

  Mount a named volume (or bind mount) at `/data` so logs and model weights
  survive container restarts instead of resetting every time. `/data` is
  deliberately a *separate* volume from `/vault`: you can wipe server state
  (`docker volume rm`) without touching your vault, since they have distinct
  lifecycles.

## Remote compose deployment

The root [`compose.yaml`](../compose.yaml) runs exomem as an always-on HTTP
service with optional tunnel-sidecar ingress, matching the remote tier
documented in [deployment.md](deployment.md).

1. Clone the repo — compose needs `compose.yaml` and a filled-in `.env`; it
   pulls the published image, so no local Python/`uv` install is required:

   ```bash
   git clone https://github.com/Artexis10/exomem.git
   cd exomem
   cp env.example .env
   ```

2. Fill in `.env` (`env.example` documents every key, including the
   compose-only ones below — uncomment the ones you need):
   - The OAuth vars from [deployment.md](deployment.md): `EXOMEM_BASE_URL`,
     `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, `EXOMEM_GITHUB_USERNAME`,
     `EXOMEM_GITHUB_USER_ID`, and `EXOMEM_JWT_SIGNING_KEY`. HA deployments also
     require matching non-empty `EXOMEM_LEASE_COORDINATOR_TOKEN`,
     `EXOMEM_WRITER_LEASE_TOKEN`, and `EXOMEM_OAUTH_STORAGE_TOKEN` values.
   - `EXOMEM_VAULT_HOST_PATH` is the **host** path bind-mounted to `/vault`
     (`compose.yaml` maps it in; the container always sees `/vault`). Compose
     fails fast with `set in .env` if it is missing.
   - Whichever tunnel credential you're using: `CLOUDFLARE_TUNNEL_TOKEN`, or
     `NGROK_AUTHTOKEN` + `NGROK_DOMAIN`.

3. Pick the runtime image:

   ```bash
   # lean keyword/BM25 (default)
   docker compose up -d

   # CPU hybrid search
   docker compose -f compose.yaml -f compose.ml.yaml up -d

   # NVIDIA/Linux CUDA-capable hybrid search
   docker compose -f compose.yaml -f compose.cuda.yaml up -d
   ```

   The CUDA override declares `gpus: all`, so it requires Docker with the NVIDIA
   container runtime. It still boots normal/CPU-default unless `.env` sets
   `EXOMEM_MODE=performance`.

4. Add the tunnel profile you have an account for when you need public ingress:

   ```bash
   docker compose --profile ngrok up -d
   # or
   docker compose --profile cloudflared up -d
   ```

   Combine the runtime override and tunnel profile when needed:

   ```bash
   docker compose -f compose.yaml -f compose.cuda.yaml --profile cloudflared up -d
   ```

   With **no** `--profile` flag, only the `exomem` service starts: no host
   port published, no tunnel sidecar running — useful for a LAN-only or
   locally-debugged setup. Uncomment the `ports:` line in `compose.yaml` to
   bind `127.0.0.1:8765` for local access.

5. Verify:

   ```bash
   docker compose exec exomem exomem doctor --profile remote
   ```

`docker compose ps` reports `exomem` healthy once
`GET /.well-known/oauth-protected-resource` (the same unauthenticated route
claude.ai uses for OAuth discovery) returns `200` — no separate `/health`
endpoint exists.

## Hardening

Run the container as a non-root user:

```bash
docker run --user "$(id -u):$(id -g)" ...
```

This works out of the box because both persistence paths point at `/data`
(`EXOMEM_LOG_DIR`, and on `ml`, `HF_HOME`) — as long as your user has write
access to the mounted `/data` volume, nothing in the image requires root. With
a **bind mount** at `/data` that's automatic (you own the host directory); a
Docker **named volume** (what `compose.yaml` uses) is initialized root-owned,
so pre-chown it once (`docker run --rm -v exomem-data:/data alpine chown -R 1000:1000 /data`)
before running non-root against it. The
image does not impose a fixed `USER`, so you can pick the uid/gid that matches
your host's volume ownership.

## Windows and macOS caveats

If you're on Windows, prefer the native install
([../QUICKSTART.md](../QUICKSTART.md)) over Docker for day-to-day editing.
Docker Desktop bind-mounts a `<drive>:\` path through WSL2, and that path does
**not** propagate `inotify` events — exomem's live file-watcher (which
re-embeds out-of-band `.md` edits) will silently miss changes made outside the
container. NTFS-backed WSL2 mounts are also noticeably slower than a native
filesystem for the same reason. Docker remains a good choice for a Linux/macOS
host, or for the remote-compose walkthrough above running on a remote Linux
box.

On Windows+WSL2 with NVIDIA, the `cuda` image can use the GPU when Docker Desktop
and the NVIDIA runtime are configured, but the bind-mount watcher caveat still
applies. For a live Obsidian vault on the Windows filesystem, native NSSM remains
the recommended default; use Docker CUDA when you accept the watcher tradeoff or
run against a Linux-native vault path.

On macOS Apple Silicon, Docker Linux containers do not expose Metal/MPS/MLX to
exomem. Use the native install for GPU-backed bge/CLIP or MLX Whisper.