Docker and Compose¶
The deploy folder holds a Docker Compose stack and Kubernetes manifests. The
stack runs the REST API, PostgreSQL for metadata, Redis, one vector store, and
optionally a Celery worker, an MCP server and Jaeger.
No Docker? lasagna serve runs the same REST API in one process. See
REST API.
You need Docker with Compose v2, make, openssl and git.
Start the stack¶
The image builds from a clone of the repository. The pip package does not carry the build context.
git clone https://github.com/dolev31/lasagna.git
cd lasagna/src/lasagna/deploy
cp .env.example .env
# The two required secrets
sed -i.bak "s/^RAG_JWT_SECRET=$/RAG_JWT_SECRET=$(openssl rand -hex 32)/" .env
sed -i.bak "s/^POSTGRES_PASSWORD=$/POSTGRES_PASSWORD=$(openssl rand -hex 16)/" .env
make up # make validate, then docker compose up -d
make health # GET /api/v1/health, pretty-printed
With .env.example as it ships, this starts Milvus, PostgreSQL, Redis, the API,
the Celery worker and beat, and the MCP server. The first make up builds the
image, which downloads the embedding and reranker models into it.
| Service | Address |
|---|---|
| REST API | http://127.0.0.1:8000 (interactive docs at /docs) |
| MCP server | http://127.0.0.1:8005/sse |
Every published port is bound to 127.0.0.1.
make up returns once the containers are created. To return only when every
service passes its healthcheck:
docker compose -f docker-compose.yml -f backends/milvus.yml up -d --wait
Choose a vector store¶
VECTOR_BACKEND names a fragment in backends/. .env.example sets it to
milvus. To use another store for one command, pass it to make:
make up VECTOR_BACKEND=qdrant
An environment variable (VECTOR_BACKEND=qdrant make up) has no effect while
.env sets it, because the Makefile includes .env and that assignment wins.
Pass the same value to make down, make logs and make ps.
VECTOR_BACKEND |
What the fragment adds |
|---|---|
milvus (default) |
A Milvus standalone container. |
milvus-lite |
No container. Milvus Lite runs in the API process. |
pgvector |
No container. The PostgreSQL service switches to the pgvector image. |
qdrant |
A Qdrant container. |
weaviate |
A Weaviate container. |
opensearch |
An OpenSearch container with its security plugin disabled. |
pinecone |
No container. Set PINECONE_API_KEY in .env. |
The image installs the client libraries for Milvus, Milvus Lite, pgvector and
OpenSearch. The qdrant, weaviate and pinecone fragments add the
vector-backends extra at build time, so make build builds the right image.
After you switch to one of those three, run make build VECTOR_BACKEND=qdrant
(or the one you chose). make validate refuses an image built without the
client and names the command.
The connection a fragment sets lives under environment:, so a QDRANT_URL in
.env does not reach the services. To use a store outside the stack, edit the
fragment or write your own.
Milvus Lite runs alone¶
Milvus Lite is a file that one process opens. With VECTOR_BACKEND=milvus-lite,
the API runs one worker, serves MCP itself at /mcp/sse on port 8000 and
processes uploads in its own threads. The worker, beat and MCP containers could
not open the file, so make validate refuses milvus-lite together with the
celery or mcp profile. Run it with no profiles:
make up VECTOR_BACKEND=milvus-lite COMPOSE_PROFILES=
Choose the services¶
COMPOSE_PROFILES in .env picks the optional services. .env.example sets it
to celery,mcp.
| Service | Profile | What it does |
|---|---|---|
api |
always | The REST API, on port 8000. |
postgres |
always | Metadata: documents, citations, profiles and sessions. |
redis |
always | Cache, rate-limit buckets and the Celery broker. Not published on the host. |
celery-worker |
celery |
Processes uploaded documents, four at a time. |
celery-beat |
celery |
Schedules the periodic garbage collection and orphan scans. |
mcp |
mcp |
A standalone MCP server over SSE, on port 8005. |
jaeger |
tracing |
Trace UI on port 16686. |
Without the mcp profile, no MCP server runs, except with milvus-lite.
Turn on auth before you publish the API¶
The stack starts with RAG_REQUIRE_AUTH=false and publishes the API on
127.0.0.1. In that mode, anything that can reach the port can read and delete
any session's documents. To publish the API beyond loopback, set these in .env
and restart:
cat >> .env <<EOF
API_BIND=0.0.0.0
RAG_REQUIRE_AUTH=true
RAG_API_KEYS=$(openssl rand -hex 32)
EOF
make up
A client then asks for a session token with one of the keys. See Authentication. The deployment README lists what else auth changes. SECURITY.md says what authentication does and does not protect.
CPU or GPU image¶
The image runs the CPU-only build of torch. For a host with an NVIDIA GPU, build with the CUDA build instead:
make build TORCH_VARIANT=cuda
make up reuses an image that exists, so switching variants needs make build.
The API, worker and MCP containers also need GPU access: the NVIDIA Container
Toolkit on the host, and a gpus: all entry on those services.
Make targets¶
| Target | What it does |
|---|---|
make up |
Run make validate, then start the stack. |
make validate |
Check the config. It stops when a required secret is empty, the backend name is wrong, or the profiles conflict with milvus-lite. |
make down |
Stop the stack and keep the volumes. |
make down-v |
Stop the stack and delete its volumes: documents, metadata and uploads. |
make build |
Rebuild the image. |
make logs |
Follow every log. make logs SVC=celery-worker follows one. |
make ps |
List the containers. |
make health |
Call GET /api/v1/health on the API port. |
make shell |
Open bash in the API container. |
make test |
Check the files and the merged compose config. It starts no container. |
make help |
List the targets. |
Healthchecks¶
| Service | Check |
|---|---|
api |
GET /api/v1/health/ready, with 300 seconds to load the models on a cold start. |
mcp |
GET /health on port 8005. |
redis, postgres |
redis-cli ping and pg_isready. |
milvus, qdrant, weaviate, opensearch |
The store's own health endpoint. |
The API, worker, beat and MCP services start after Redis and PostgreSQL are healthy.
Kubernetes¶
The k8s/ folder holds a Kustomize base and one overlay per vector store.
overlays/local.example is the template for the overlay that holds your secrets.
See the
deployment README
for the manifests.