Skip to main content

Kubernetes

ClawQL is usually deployed as clawql-mcp-http: Streamable HTTP MCP on /mcp, GraphQL on /graphql, and (when ENABLE_GRPC=1) gRPC protobuf MCP on port 50051—all in one pod. values-docker-desktop.yaml matches production patterns: with ingress-nginx, http://clawql-mcp.localhost/mcp; with default Istio (make local-k8s-up), ingress-nginx is skipped and svc/clawql-mcp-ingress is a LoadBalancer → localhost on Docker Desktop / Rancher Desktop (pod hostNetwork would bind inside the Kubernetes VM only—see docker/README.md). svc/clawql-mcp-http is often ClusterIP under ambient Istio; MCP HTTP is http://localhost/mcp or http://clawql-mcp.localhost/mcp via Gateway + VirtualService. gRPC uses localhost:50051 through the same ingress Service when enabled. Remote clusters often use Kustomize dev / prod with scripts/deploy/deploy-k8s.sh.

Local MCP on Docker Desktop

Use this when you want the same north-south pattern as productionIngress + hostname (http://clawql-mcp.localhost/mcp) for Cursor or other MCP clients, without running npx in a terminal.

Prerequisites

  • Docker Desktop with Kubernetes enabled (Settings → Kubernetes).
  • kubectl and Helm 3 on your PATH (Helm is required for Kyverno on every local-k8s-up path, including Kustomize).
  • Repo clone at the version you want to run.

One-shot install

From the repository root:

make local-k8s-up

Or:

bash scripts/kubernetes/local-k8s-docker-desktop.sh

Default: helm upgrade --install with charts/clawql-mcp/values-docker-desktop.yaml: signed ghcr.io/danielsmithdevelopment/clawql-mcp:latest, ghcr.io/danielsmithdevelopment/clawql-website:latest, and ghcr.io/danielsmithdevelopment/clawql-dashboard:latest, all-providers, Obsidian memory vault.hostPath at $HOME/.ClawQL (Helm key vault — not HashiCorp Vault; override CLAWQL_LOCAL_VAULT_HOST_PATH). With default Istio, chart Ingress objects are off and svc/clawql-mcp-http is ClusterIP; north-south HTTP is clawql-mcp-ingress (LoadBalancer on docker/rancher kube contexts). With CLAWQL_LOCAL_K8S_ISTIO=0, Ingress serves clawql-mcp.localhost and svc/clawql-mcp-http stays LoadBalancer for gRPC / diagnostics. The script installs Kyverno and a ClusterPolicy so those images must pass Cosign verification in the clawql namespace (unsigned local docker build deploys are not supported).

Kustomize: CLAWQL_LOCAL_K8S_INSTALLER=kustomize make local-k8s-upkubectl apply -k docker/kustomize/overlays/local for the MCP workload; Helm still installs Kyverno and applies the ClusterPolicy template from the chart.

For dashboard-specific Helm values and Vault sync wiring, see Dashboard on Kubernetes.

Optional Istio (Docker Desktop — default on): CLAWQL_LOCAL_K8S_ISTIO=ambient or sidecar runs scripts/kubernetes/install-istio-docker-desktop.sh before ClawQL (Helm Istio + istio/gateway clawql-mcp-ingress, Istio Gateway + VirtualService for MCP on :80 / :50051, PeerAuthentication STRICT). ingress-nginx is omitted unless you force it; clawql-mcp-ingress uses LoadBalancer → localhost on docker-desktop / rancher-desktop contexts. Prefer http://localhost/mcp when the gateway is installed. The same path installs Prometheus, Kiali, Grafana, Grafana Tempo, optional Grafana Loki, and a ClawQL OpenTelemetry Collector in istio-system (unless you opt out — see Docker Desktop: Istio & observability and docker/README.md).

How this ties to CI: full golden image and admission story — golden-image-pipeline.md and Security.

Endpoints and client config

  • MCP (Streamable HTTP): http://localhost/mcp or http://clawql-mcp.localhost/mcp (default Istio vs ingress-nginx — same /mcp shape as prod)
  • Health: curl -s http://localhost/healthz or http://clawql-mcp.localhost/healthz
  • Bundled docs UI: http://docs.localhost (Istio VirtualService when gateway is on; Ingress when Istio is off)
  • Bundled dashboard UI: http://clawql.localhost (Helm-managed service; Istio VirtualService when gateway is on, Ingress when Istio is off)

Point your MCP client at url (not stdio command). Example shape:

{
  "mcpServers": {
    "clawql": {
      "url": "http://localhost/mcp"
    }
  }
}

Same url shape as production (https://mcp.example.com/mcp); only scheme and host differ locally.

Optional gRPC on the same deployment

When ENABLE_GRPC=1 (and optionally ENABLE_GRPC_REFLECTION=1 for grpcurl list), the container listens on 50051 for grpc.health.v1, model_context_protocol.Mcp, and mcp.transport.v1.Mcp.Session. The clawql-mcp-http Service includes a grpc port (50051) alongside HTTP in the Helm chart and Kustomize base / dev / prod—verify with kubectl -n clawql get svc clawql-mcp-http. For native grpc readiness/liveness probes on kubelet 1.27+, use the opt-in overlay docker/kustomize/overlays/grpc-enabled/ (see root README Kubernetes section). Full port matrix: docs/deployment/deploy-k8s.md — Service ports.

What the Helm install sets

  • Namespace: clawql
  • CLAWQL_PROVIDER: all-providers (Google Cloud bundled APIs + every other bundled vendor; same as bare npx clawql-mcp with no spec env). Override via values-docker-desktop.yaml or helm --set provider=....
  • Kube context: the script prefers docker-desktop when present so you do not accidentally deploy to another cluster.

Cold start can take a while while specs load. Wait until /healthz returns {"status":"ok",...} or the pod is Ready before the client connects.

MCP auth tokens (merged default bundle)

For local Docker Desktop k8s, execute needs tokens in the clawql-mcp-http pod. Per-vendor env vars match src/auth-headers.ts: GitHub (CLAWQL_GITHUB_TOKEN), Cloudflare (CLAWQL_CLOUDFLARE_API_TOKEN), Google (GOOGLE_ACCESS_TOKEN / CLAWQL_GOOGLE_ACCESS_TOKEN), Slack and other fallbacks (CLAWQL_BEARER_TOKEN / CLAWQL_HTTP_HEADERS), Paperless (PAPERLESS_API_TOKEN), Stirling (STIRLING_API_KEY), optional bearer for Tika/Gotenberg. The same GitHub PAT is often stored as CLAWQL_BEARER_TOKEN for merged-bundle fallback.

One-shot: from the repo root, with gh logged in or values in .env:

bash scripts/kubernetes/k8s-docker-desktop-set-mcp-auth.sh

Pipe a PAT: gh auth token | bash scripts/kubernetes/k8s-docker-desktop-set-mcp-auth.sh. Set CLAWQL_LOAD_DOTENV=0 to skip sourcing .env.

This creates/updates Secret clawql-github-auth (name kept for backward compatibility), attaches keys to deployment/clawql-mcp-http, and restarts. Full table and manual kubectl steps: docker/README.md (section MCP auth). The old script name k8s-docker-desktop-set-github-token.sh is a deprecated alias.

helm upgrade --install reapplies chart values; env injected only with kubectl set env can be overwritten on the next upgrade. Prefer helm --set extraEnv or a Secret wired through values.yaml for durable config.

Rebuild after code changes

Kubernetes on Docker Desktop expects signed images from GHCR. After upstream publishes a new tag, run make local-k8s-up again (or helm upgrade with image.tag / digest). For rapid local edits without admission, use make local-docker-up. Example recycle:

make local-k8s-up
kubectl --context docker-desktop -n clawql rollout restart deployment/clawql-mcp-http

Teardown

helm uninstall clawql -n clawql

Or delete the whole namespace:

kubectl --context docker-desktop delete namespace clawql

Remote clusters (dev / prod)

For registries and image tags (not the local latest flow), use overlays and the deploy script:

ENV=dev IMAGE=us-central1-docker.pkg.dev/<project>/<repo>/clawql-mcp TAG=<tag> make deploy-k8s

Or install the Helm chart with your image registry. Full checklist: docs/deployment/deploy-k8s.md.

More detail in the repo

  • Container build, Compose, and extended K8s notes: docker/README.md
  • Starter manifest: docker/kubernetes-starter.yaml

NATS JetStream quick checklist

Use this when enabling in-cluster event streaming for Ouroboros/agent/edge coordination (#127):

  1. Enable NATS in Helm values (nats.enabled=true) and keep nats.service.type=ClusterIP unless external exposure is required.
  2. Enable persistence for durable streams (nats.persistence.enabled=true) and right-size nats.jetStream.maxFileStore.
  3. Confirm NATS service health (/healthz on monitor port 8222) before enabling dependent workloads.
  4. Use nats.url only when pointing ClawQL at an external/shared NATS cluster.
  5. Keep subject naming consistent across services (clawql.workflow.*, clawql.agent.*, clawql.edge.*).
  6. Validate CLAWQL_NATS_URL / CLAWQL_NATS_JETSTREAM env vars landed in clawql-mcp-http after helm upgrade.

Full values reference and runbook: NATS JetStream. Helm context: Helm.

Use this when enabling in-cluster Flink for real-time Onyx index freshness (#119):

  1. Enable Flink in Helm values (flink.enabled=true) and keep flink.service.type=ClusterIP.
  2. Put connector credentials in a dedicated Secret and wire it with flink.connectorSecret.
  3. Confirm JobManager + TaskManagers are ready in the same namespace as ClawQL.
  4. Validate connector throughput and Onyx ingestion latency before scaling task slots/replicas.
  5. Keep Flink UI private by default; use temporary kubectl port-forward for debugging.

Full architecture, values reference, and runbook: Flink Onyx sync. Helm context: Helm.