| title | Docker |
|---|
Uteke ships as a lightweight multi-arch Docker image (~10MB). The embedding model (~200MB) downloads automatically on first run and is cached in the volume, so subsequent updates are instant.
β οΈ Security: The default config listens on127.0.0.1(localhost only). For network access, setUTEKE_AUTH_TOKEN(see Authentication).
# Pull and run (GHCR)
docker run -d --name uteke \
-p 127.0.0.1:8767:8767 \
-v uteke-data:/data \
ghcr.io/codecoradev/uteke:latest
# Or pull from Docker Hub
docker run -d --name uteke \
-p 127.0.0.1:8767:8767 \
-v uteke-data:/data \
codecoradev/uteke:latest
# Verify it's running
curl http://localhost:8767/health
# Store a memory
curl -X POST http://localhost:8767/remember \
-H "Content-Type: application/json" \
-d '{"content": "Deployed v2.0 to production"}'
# Recall
curl -X POST http://localhost:8767/recall \
-H "Content-Type: application/json" \
-d '{"query": "deployment"}'# Clone and use the included docker-compose.yml
docker compose up -d
# Or create your own:
cat > docker-compose.yml << 'EOF'
services:
uteke:
image: ghcr.io/codecoradev/uteke:latest
ports:
- "127.0.0.1:8767:8767"
volumes:
- uteke-data:/data
restart: unless-stopped
volumes:
uteke-data:
EOF
docker compose up -d| Variable | Default | Description |
|---|---|---|
UTEKE_HOME |
/data |
Data directory (set in Dockerfile) |
UTEKE_AUTH_TOKEN |
β | Bearer token for API authentication |
UTEKE_NAMESPACE |
default |
Default namespace |
UTEKE_READ_ONLY_TOKEN |
β | Bearer token that is only allowed to read (GET and POST /recall) |
UTEKE_EMBEDDING_BACKEND |
onnx |
onnx (local model), openai (any OpenAI-compatible endpoint) or ollama. Read by the server since v0.20.1 |
UTEKE_EMBEDDING_BASE_URL |
β | Endpoint of the external embedder, e.g. http://embor:8355/v1 |
UTEKE_EMBEDDING_MODEL |
β | Model name sent to the endpoint |
UTEKE_EMBEDDING_DIMS |
backend default | Vector size; must match the existing store |
UTEKE_EMBEDDING_API_KEY |
β | Key for the endpoint (also read from OPENAI_API_KEY) |
UTEKE_EMBEDDING_ENDPOINT_PATH |
/embeddings |
Path appended to the base URL |
# Read token securely (not stored in shell history)
read -s UTEKE_AUTH_TOKEN
export UTEKE_AUTH_TOKEN
docker run -d --name uteke \
-p 127.0.0.1:8767:8767 \
-v uteke-data:/data \
-e UTEKE_AUTH_TOKEN \
ghcr.io/codecoradev/uteke:latest
# Now all requests need Authorization header
curl -H "Authorization: Bearer $UTEKE_AUTH_TOKEN" \
http://localhost:8767/healthCompose reads a .env file only to fill in ${VAR} references inside the
compose file. Nothing in .env reaches the container by itself. Pass what the
container needs in one of two ways:
services:
uteke:
image: ghcr.io/codecoradev/uteke:latest
# (a) load every variable of .env into the container ...
env_file: .env
environment:
# (b) ... or list them one by one; ${VAR} is filled from .env
- UTEKE_AUTH_TOKEN=${UTEKE_TOKEN}The server reads UTEKE_AUTH_TOKEN; a variable called UTEKE_TOKEN in .env
does nothing unless it is mapped like above. If the token never arrives the
server still starts, now without authentication, and the only hint is this
line in the log:
WARN uteke_serve: Authentication: disabled β set --auth-token or UTEKE_AUTH_TOKEN for production
Check the log after every change to the compose file or .env, and recreate the
container (docker compose up -d --force-recreate) so the new environment is used.
By default the image downloads a ~208 MB ONNX model into /data/models on the
first start. With an external embedder none of that is needed:
UTEKE_EMBEDDING_BACKEND=openai
UTEKE_EMBEDDING_BASE_URL=http://embor:8355/v1
UTEKE_EMBEDDING_MODEL=embeddinggemma-q4
UTEKE_EMBEDDING_DIMS=768
# UTEKE_EMBEDDING_API_KEY=... # if the endpoint needs a bearer token(Load them with env_file as above, or put an [embedding] section in
/data/uteke.toml; the entrypoint and the server both read it, and the
environment variable wins over the file.) This needs v0.20.1 or later: earlier
servers ignored the setting and always used the local model.
- The log should say
Embedding backend is 'openai': skipping the local ONNX model download.andEmbedding backend: openai. If it saysdownloading embedding modelinstead, the variables did not reach the container. UTEKE_EMBEDDING_DIMSmust equal the vector size of your store. The URL ends in/v1and uteke appends/embeddings(seeUTEKE_EMBEDDING_ENDPOINT_PATH).- The embedder must be reachable from the container (same Docker network). If it
is down,
rememberstill stores the memory but answers"embedding_written": falsewith a warning, andrecallfails until it is back. - Check it with one write:
POST /remembershould return"embedding_written": trueand"warning": null.
The data directory still has to be writable: the image runs as uteke (uid and
gid 1000) and writes uteke.db, the index files and embed_cache.db to /data
whatever the embedder. For a bind mount, chown -R 1000:1000 the host directory.
Data is stored in the /data volume. Mount it for persistence:
# Named volume (managed by Docker)
docker run -v uteke-data:/data ...
# Bind mount (explicit path)
docker run -v /path/to/uteke:/data ...The volume contains:
uteke.dbβ SQLite database (memories, metadata, FTS5)uteke_index.usearchβ HNSW vector index (default engine)uteke_index.vecqβ quantized index (created if you switch engines)uteke_index.keysβ Index key mappingmodels/embeddinggemma-q4/β ONNX embedding model (~200MB; only with theonnxbackend)
Both engines ship in the image. Pick one via env var β switching leaves your data untouched; the new engine rebuilds its index from SQLite on the next start:
environment:
- UTEKE_VECTOR_BACKEND=vecq # or "usearch" (default)usearch (HNSW) has the best query latency at scale; vecq (4-bit + residual,
pure Rust) builds indexes ~16x faster with ~3x smaller files. See
configuration for details.
Images are built for:
linux/amd64β Intel/AMD serverslinux/arm64β Apple Silicon, ARM servers (Ampere, Graviton)
Docker automatically pulls the correct architecture.
| Registry | Image |
|---|---|
| GitHub Container Registry | ghcr.io/codecoradev/uteke:latest |
| Docker Hub | codecoradev/uteke:latest |
| Tag | Description |
|---|---|
latest |
Latest stable release |
v0.12.0 |
Specific version |
0.12 |
Minor version (latest patch) |
slim |
Slim image (no embedded model β mount model volume separately, see below) |
The container runs uteke-serve by default. To run CLI commands:
# Run a one-off CLI command
docker exec uteke uteke recall "deployment" --limit 5
# Or override the entrypoint
docker run --rm -v uteke-data:/data \
--entrypoint uteke \
ghcr.io/codecoradev/uteke:latest \
statscurl http://localhost:8767/health
# β {"status":"healthy","memories":42,"index_size":1024}Docker Compose includes a built-in health check (curl is pre-installed in the image):
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8767/health"]
interval: 30s
timeout: 5s
retries: 3The Docker image includes the uteke-mcp binary for MCP-based AI agent integration.
HTTP transport is available through uteke-serve at the /mcp endpoint. Start the container normally and point your MCP client at the server:
# Start uteke with MCP endpoint enabled (default)
docker run -d --name uteke \
-p 127.0.0.1:8767:8767 \
-v uteke-data:/data \
ghcr.io/codecoradev/uteke:latest
# The MCP endpoint is available at:
# http://localhost:8767/mcp (Streamable HTTP transport)Example client configurations:
// Cursor β .cursor/mcp.json
{
"mcpServers": {
"uteke": {
"url": "http://localhost:8767/mcp"
}
}
}Note: When running uteke in Docker and the MCP client on the host,
localhostworks because of the port mapping. For remote setups, replacelocalhostwith the server's hostname or IP and configureUTEKE_AUTH_TOKEN.
The uteke-mcp binary in the container provides stdio transport for clients that require subprocess-based MCP. Use --entrypoint to run it:
docker run --rm -v uteke-data:/data \
--entrypoint uteke-mcp \
-i ghcr.io/codecoradev/uteke:latestFor Claude Desktop or Cursor with stdio transport:
// Claude Desktop β claude_desktop_config.json
{
"mcpServers": {
"uteke": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-v", "uteke-data:/data",
"--entrypoint", "uteke-mcp",
"ghcr.io/codecoradev/uteke:latest"
]
}
}
}Both transports expose the same tools (MCP protocol version 2025-06-18):
| Tool | Description |
|---|---|
uteke_remember |
Store a memory (supports type, room, author, tags) |
uteke_recall |
Semantic search (supports tags filter, min_score) |
uteke_search |
Text search with optional tag filter |
uteke_list |
List memories (supports pagination via offset) |
uteke_forget |
Delete a memory |
uteke_stats |
Memory store statistics |
uteke_context |
AI-optimized context output for prompts |
uteke_dream |
One-command maintenance pipeline (lint β backlinks β dedup β orphans) |
uteke_doc_create |
Create a document (wiki/knowledge base entry) |
uteke_doc_get |
Retrieve a document by ID |
uteke_doc_list |
List all documents |
uteke_doc_search |
Search documents |
uteke_doc_delete |
Delete a document |
uteke_doc_update |
Partial document update with chunk rebuild (#589) |
uteke_doc_move |
Move document to new parent (#438) |
uteke_graph |
Get nodes + edges JSON for visualization |
uteke_room_recall |
Semantic recall within a room |
uteke_room_memories |
List memories in a room (#569) |
uteke_room_create |
Create a room |
uteke_room_delete |
Delete a room |
uteke_room_stats |
Room statistics |
uteke_room_summary |
Room topic summary (tag clustering, no LLM) |
uteke_room_summary_document |
Generate summary document from room (β POST /room/summary-document) |
uteke_tags_list |
List all tags with counts (#566) |
uteke_tags_rename |
Rename a tag across all memories (#566) |
uteke_tags_delete |
Delete a tag from all memories (#566) |
uteke_pin |
Pin a memory (prevent decay) (#566) |
uteke_unpin |
Unpin a memory (#566) |
See TLS & Reverse Proxy for Caddy, Nginx, and Cloudflare Tunnel setup.