Production deployment paths for Kazma: Docker Compose (primary), Kubernetes (Hub service), Windows native, and server management. Honest notes on what each artifact actually deploys.
A separate Hub API service — not the main Kazma agent.
⚠ See §4.
Cloudflare Pages / edge workers
—
❌ Not applicable. Kazma is a Python/uvicorn server, not an edge deployment.
Bare uvicorn
The main agent.
✅ kazma serve / kazma-web.
Honest note: Older tasking mentioned “Cloudflare Pages, serverless edge workers.” Kazma is a stateful Python service (LangGraph + SQLite + optional ChromaDB). It is not designed for serverless/edge deployment. The MCP tooling skills in this environment cover Cloudflare Workers, but they are unrelated to deploying Kazma itself.
The live file is Dockerfile at the repo root — do not copy-paste a snapshot here (it drifted before: missing git, Arabic fonts, LibreOffice, Tesseract, ClamAV, and document-platform). What it actually does:
Base: python:3.11-slim.
System deps: git, libpq, Noto Arabic fonts, LibreOffice, Tesseract (eng+ara), ClamAV, build-essential, curl.
# Development / single-host — use kazma serve (not raw uvicorn on Windows)
pipinstall-e".[rag,dev,tui]"
kazmaserve# 127.0.0.1:9090
For a public-facing host behind a reverse proxy:
Terminal window
# ONLY with KAZMA_SECRET set does `kazma serve` bind 0.0.0.0
KAZMA_SECRET=$(opensslrand-hex32) \
KAZMA_TRUSTED_PROXIES=127.0.0.1 \
kazma serve
Never expose 0.0.0.0 without KAZMA_SECRET. The HITL approval endpoint would otherwise be unauthenticated. Put Kazma behind nginx/Caddy/Traefik with TLS and let the proxy hold the public socket.
KAZMA_TRUSTED_PROXIES is required whenever a reverse proxy is in front of Kazma. Set it to the address the proxy connects from — 127.0.0.1 for a same-host nginx/Caddy, or the container/bridge IP in Docker.
Without it, request.client.host is the proxy for every request. A same-host proxy makes every internet visitor look like 127.0.0.1, which Kazma treats as the local operator and auto-issues an admin session to — a complete auth bypass over both HTTP and WebSocket (audit F-01, fixed 2026-08-29). With it set, Kazma reads the real client from X-Forwarded-For and stops trusting peer address as a credential.
Your proxy must set the forwarded headers, and must overwrite rather than append a client-supplied value. The shipped deploy/nginx-ha.conf already does:
These deploy a Hub service, not the main Kazma agent. The image kazma/hub-api:latest is not built from this repo’s Dockerfile and is not published from here.
The referenced infrastructure does not match the main codebase. The main Kazma agent uses SQLite (WAL) everywhere (ConfigStore, checkpointer, TaskStore, snapshots, vector memory via ChromaDB). It does not read DATABASE_URL or REDIS_URL, and has no PostgreSQL or Redis client. These env vars belong to a separate (aspirational or external) Hub API service.
The health path /api/v1/health differs from the main app’s /health/live and /health/ready (kazma-ui/.../health.py:94,104).
Resource limits (256Mi–512Mi, 250m–500m) are reasonable for a stateless API but too small for the main agent if ChromaDB + sentence-transformers are loaded (those alone can exceed 512 Mi).
Recommendation: treat kubernetes/ as a starting point for deploying a separate Hub API. To deploy the main Kazma agent on Kubernetes, write a new manifest using the repo’s Dockerfile, the /health/live probe, an explicit KAZMA_SECRET, a PVC for kazma-data/ and the vector path, and resource limits adequate for the RAG extras (≥1 Gi memory).
Cross-platform path policy and data layout: Portability.
setup.ps1 is the deterministic, fail-fast, idempotent Windows bootstrap (PowerShell 5.0+). It:
Validates the environment (Python 3.11+, uv, kazma.yaml).
Syncs the virtual environment from pyproject.toml.
Runs a foundation integrity check (core imports + test collection).
Terminal window
.\setup.ps1
.\setup.ps1 -Debug # verbose
PowerShell rule (from AGENTS): never chain commands with && or ||. Use ; and check $LASTEXITCODE. The Bash tool in this environment uses Git Bash, not PowerShell.
On a host watched by KazmaAgent (the health-gated supervisor), pick up
code with one command. Do not kill python/uvicorn by hand — that
fights the guard (stale port holder or a 180s false “never ready”).
Wait for Kazma is up. build … (first boot can take a few minutes for
imports / MCP / Postgres; budget 900s). --reload plants a flag before
killing serve.py, so the long-lived guard does not treat that kill as
a crash and climb the backoff ladder (5s → 300s). If --reload still sits
on WinError 10061 for minutes, the guard process is still running old
code — restart KazmaAgent once (schtasks /End then /Run KazmaAgent
on Windows). --status shows whether the watcher and /health/ready
agree. --install registers the OS task (install_service.py).
On Windows, start via kazma serve or the guard — not python -m uvicorn.
Uvicorn 0.36+ hardcodes ProactorEventLoop, and psycopg-async then cannot
open the Postgres checkpointer.
KAZMA_SECRET set (strong random) — required to protect /api/approve.
Server bound to 127.0.0.1 (or behind a TLS-terminating reverse proxy).
KAZMA_TRUSTED_PROXIES set to the proxy’s address whenever a reverse proxy is in front — otherwise every visitor is treated as the local operator (audit F-01). Verify with curl -s https://your-host/api/auth/status: authenticated must be false before login.
Provider API keys rotated if this instance ever served /api/settings on a build before 2026-08-29 (audit F-02 leaked them in plaintext).
Volumes persisted for kazma-data/ and the vector memory path.
kazma.yamlsafety.hitl.enabled: true and a complete require_approval_for list.
All three HITL build sites pass hitl_config (default builds do; verify any custom build).
MCP stdio servers sandboxed (no auth on stdio transport).
Skills signed (kazma hub sign) with the same KAZMA_SECRET used at load time.
Resource limits account for ChromaDB + sentence-transformers if RAG is enabled (≥1 Gi).
Health check wired (/api/gateway/status or /health/live).
Logs shipping to your collector (JSON format available via logging.format: json).
The repo’s notes mention “resource constraints on 24 GB VRAM setups.” Practical guidance:
sentence-transformers (BAAI/bge-m3) is CPU-friendly (~2.2 GB) — it does not need a GPU. VRAM is only relevant if you point Kazma at a local GPU model server (Ollama/LM Studio/vLLM).
For local LLM inference, the model server (not Kazma) owns the VRAM budget. Kazma itself is a lightweight httpx client to that server.
ChromaDB is memory-mapped; size the vector volume accordingly.
Kazma pushes a status update to every configured platform (Telegram/Discord/Slack) when the server starts, restarts, shuts down, or fails to boot — so you can tell from chat when something went wrong (a hung boot, a crash that emits no shutdown message, a bad bot token).
Notifications route through the SwarmMessageBus — the same bus that delivers swarm worker output. No separate notification path is constructed. The bus is wired during KazmaAppBuilder.build() (before the lifespan), and FanOutBusAdapter fans out to every configured platform. When no platform bus is configured (NullBusAdapter), the feature self-disables silently.
The messages need a destination. Set connectors.<platform>.swarm_chat_id to the chat ID where you want them delivered (typically your DM with the bot):
Terminal window
# Telegram example — set to your user ID (same as allowed_users)
kubernetes/ deploys a Hub API, not the main agent, and references PostgreSQL + Redis, which the main codebase does not use. Flagged prominently to prevent misdeployment.
Volume path mismatch (/root/.kazma/... vs the kazma user’s home) in docker-compose.yml — set KAZMA_VECTOR_PATH explicitly to be safe.
No Cloudflare/edge deployment path. Kazma is a stateful Python service; don’t attempt serverless packaging.
Health path differs between the K8s manifest (/api/v1/health) and the real app (/health/live).