From Zero to Production: Running Uteke in Docker
Deploy Uteke as a Docker container with persistent volumes, health checks, and API authentication. Production-ready in 5 minutes.
Uteke runs in Docker with one command
Pull the official image from GHCR, mount a volume for the data directory, and you have a production memory server:
docker run -d --name uteke \
-p 127.0.0.1:8767:8767 \
-v uteke-data:/data \
ghcr.io/codecoradev/uteke:latest
curl http://localhost:8767/healthThe image is about 10 MB, built for linux/amd64 and linux/arm64, and everything Uteke persists lives in the /data volume. The embedding model (around 200 MB) downloads on first run and stays cached in the volume, so later upgrades skip that download. This guide covers auth, Compose, backups, and the vector engine choice, from our own production deployments.
Why containerize a single binary
Uteke is self-contained, so Docker is not about dependencies. It is about operations: a pinned runtime you can upgrade atomically, identical behavior across your laptop and your servers, a health endpoint your orchestrator already knows how to poll, and a clean story for Compose or Kubernetes. The container wraps one process and one volume; that simplicity is the feature.
First render: store and recall through the API
Once the container is up, verify it and push a memory through the HTTP API:
curl -X POST http://localhost:8767/remember \
-H "Content-Type: application/json" \
-d '{"content": "Deployed v2.0 to production"}'
curl -X POST http://localhost:8767/recall \
-H "Content-Type: application/json" \
-d '{"query": "deployment"}'Recall answers in roughly 45 ms warm, with zero LLM tokens per query. The port binding above keeps the API on localhost; when agents on other machines need access, add authentication and a reverse proxy.
Add authentication before exposing the port
The default configuration listens on 127.0.0.1. For network access, set a bearer token and pass it into the container:
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
curl -H "Authorization: Bearer $UTEKE_AUTH_TOKEN" \
http://localhost:8767/healthReading the token with read -s keeps it out of your shell history. Every endpoint except /health then requires the Authorization: Bearer header.
Docker Compose for a persistent deployment
Compose makes the volume, the health check, and the token explicit. Put the token in a .env file next to the compose file, never in the file itself:
services:
uteke:
image: ghcr.io/codecoradev/uteke:latest
ports:
- "127.0.0.1:8767:8767"
volumes:
- uteke-data:/data
environment:
- UTEKE_AUTH_TOKEN=${UTEKE_AUTH_TOKEN}
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:8767/health"]
interval: 10s
timeout: 3s
retries: 3
volumes:
uteke-data:docker compose up -d brings it up; the health check gates restarts and orchestrator decisions.
What lives in the volume
The volume contains the SQLite database (uteke.db, memories plus metadata plus full-text search), the vector index files, index key mappings, and the cached embedding model under models/. This is why a file-level copy of the volume is a complete backup: one directory holds the entire memory store. Back it up before every upgrade.
Pick your vector engine: usearch or vecq
Since v0.17.0 both engines ship in the image, selected with one environment variable:
environment:
- UTEKE_VECTOR_BACKEND=vecq # or "usearch" (default)usearch (HNSW) has the best query latency at scale. vecq (4-bit quantization with residuals, pure Rust) builds indexes about 16x faster with roughly 3x smaller files, which matters on small VPS disks and slow volumes. Switching engines leaves your data untouched: the new engine rebuilds its index from SQLite on the next start. The quantization tradeoffs are covered in the vecq 4-bit post and the edge recall budget analysis.
Upgrades and lessons from production
Pulling a new image version is the whole upgrade: docker pull, stop, remove the container, run again with the same volume. Memories survive because they live in the volume, not the container. Three habits we picked up along the way: start with generous memory limits since HNSW indices grow with the dataset, watch disk usage on the data volume, and snapshot the volume before every version bump. Rehearse a restore once; an untested backup is a hope, not a backup.
Where to go from here
For the CLI-first path and agent hookup, start with getting started with Uteke; the MCP wiring for coding agents is in the MCP memory server post. The raw docs live in the repository under docs/docker.md, and images are on both GHCR and Docker Hub (codecoradev/uteke).
Frequently asked questions
How much RAM does Uteke need in Docker?
The container itself is tiny; memory goes to the vector index and the embedding model, which grow with your dataset. Start with a modest limit on a small VPS and raise it as the index grows.
Where are my memories stored in the container?
In the /data volume: the SQLite database, the vector index files, and the cached embedding model. Nothing you care about lives in the container layer, so recreating the container is safe.
How do I upgrade the Uteke container without losing data?
Pull the new image, stop and remove the old container, and run the new one with the same volume. Memories live in the volume and carry over. Snapshot the volume first anyway.
Can I run Uteke in Docker on ARM servers?
Yes. Images are multi-arch (linux/amd64 and linux/arm64), and Docker pulls the matching architecture automatically, so Raspberry Pi-class boards and Graviton instances use the same command.