Deployment
Last updated April 7, 2026
Running KB Labs in dev, single-host production, and distributed deployments.
KB Labs is a handful of Node processes plus some stateful infrastructure (Qdrant, Redis, optionally Postgres). Deployment boils down to running each process somewhere, wiring them together through the gateway, and pointing them at a shared kb.config.json.
This page walks through the deployment modes in order of complexity: local dev, single-host production, and distributed production. For the gateway-specific deployment details (TLS, reverse proxies, scaling), see Gateway → Self-Hosted.
Architecture recap
┌─────────────────┐
│ Gateway │ ← only process exposed externally
│ :4000 │
└────────┬────────┘
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌─────────────┐
│ REST │ │ Workflow │ │ Marketplace │
│ :5050 │ │ :7778 │ │ :5070 │
└────┬────┘ └─────┬────┘ └──────┬──────┘
│ │ │
└─────────┬───────┴──────────────────┘
│
┌───────┴────────┐
│ State daemon │ ← optional, for distributed setups
│ :7777 │
└────────┬───────┘
│
┌─────────┴──────────┐
▼ ▼
┌─────────┐ ┌──────────┐
│ Qdrant │ │ Redis │
│ :6333 │ │ :6379 │
└─────────┘ └──────────┘Five platform processes (gateway, REST, workflow, marketplace, state daemon), two stateful infrastructure services (Qdrant, Redis), and one optional UI (Studio). Everything else — plugins, adapters, Studio pages — rides inside these processes.
Mode 1 — Local dev
The lowest-overhead setup. Use for plugin development and testing.
Start everything
kb-dev startkb-dev reads .kb/dev.config.json, resolves the service dependency graph, starts Docker containers for infrastructure, and spawns Node processes for the platform services. Status:
kb-dev statusShows each service with its port, health state, and URL. Once everything is green you can access Studio at http://localhost:3000, the REST API at http://localhost:5050/api/v1/health, etc.
Start only what you need
kb-dev start infra # just Qdrant, Redis, state-daemon
kb-dev start rest # REST API + its dependencies
kb-dev start studio # Studio + its dependencies (boots everything it needs)The dependency graph is respected automatically. Starting studio boots gateway, rest, workflow, and everything transitively required.
Dev config
Local dev uses .kb/dev.config.json for service management and .kb/kb.config.json for platform config. Both files live in the workspace root. See Configuration → dev.config.json and Configuration → kb.config.json for the full schemas.
Dev mode reads .env automatically, so put your OPENAI_API_KEY, QDRANT_URL, and other secrets there.
Mode 2 — Single-host production
One server running all services. Fine for small deployments — a team of a few developers, an internal tool, a proof of concept. Scales to a few dozen concurrent users before you need to split services across hosts.
Setup
- Install Node 20+ and
pnpm. - Clone the KB Labs workspace (or the subset you need — gateway, REST, workflow, marketplace).
- Build:
pnpm install && pnpm buildin each service package. - Install infrastructure:
- Qdrant (Docker or standalone binary) on port 6333.
- Redis on port 6379.
- Write
kb.config.jsonat the deployment root. Declare your adapters and their options. See Configuration → kb.config.json. - Set environment variables:
GATEWAY_JWT_SECRET— required for production.GATEWAY_INTERNAL_SECRET— required if container-mode execution is enabled.NODE_ENV=production.OPENAI_API_KEYor whatever your LLM adapter needs.
Running
The simplest approach: launch each service as a background process under systemd, pm2, or your init system of choice. Sample systemd unit for the REST API:
# /etc/systemd/system/kb-rest.service
[Unit]
Description=KB Labs REST API
After=network.target kb-qdrant.service kb-redis.service
[Service]
Type=simple
User=kb-labs
WorkingDirectory=/opt/kb-labs
ExecStart=/usr/bin/node ./platform/kb-labs-rest-api/apps/rest-api/dist/index.js
Environment=NODE_ENV=production
Environment=PORT=5050
Environment=KB_PROJECT_ROOT=/opt/kb-labs
EnvironmentFile=/etc/kb-labs/secrets.env
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.targetRepeat for workflow, marketplace, gateway, and state-daemon. Enable and start them in dependency order:
sudo systemctl enable --now kb-qdrant kb-redis kb-state-daemon
sudo systemctl enable --now kb-workflow kb-rest kb-marketplace
sudo systemctl enable --now kb-gatewayReverse proxy
Put Caddy, nginx, or Traefik in front of the gateway to terminate TLS and handle rate limiting. See Gateway → Self-Hosted for full examples.
Persistence
Single-host deployments can use SQLite for everything — db, documentDb, analytics, logs. Put the SQLite files on a persistent volume (not a tmpfs or container-local FS) and back them up regularly.
Mode 3 — Distributed production
Services running on different hosts or in a container orchestrator (Kubernetes, Nomad, ECS). Use when you need horizontal scaling, isolation between services, or redundancy.
Architecture
Each platform service becomes its own deployable unit. The gateway fronts them all; internal traffic flows on an internal network (or within a VPC); external clients hit the gateway.
Container images
Every service that ships a ServiceManifest has a Dockerfile you can build from:
platform/kb-labs-rest-api/Dockerfileplatform/kb-labs-workflow/packages/workflow-daemon/Dockerfile(check for one)platform/kb-labs-marketplace/Dockerfileinfra/kb-labs-gateway/apps/gateway-app/Dockerfile
For services without a pre-made Dockerfile, use a multi-stage Node image pattern:
FROM node:20-slim AS build
WORKDIR /build
COPY . .
RUN pnpm install --frozen-lockfile
RUN pnpm build
FROM node:20-slim
WORKDIR /app
COPY --from=build /build/dist ./dist
COPY --from=build /build/node_modules ./node_modules
COPY --from=build /build/package.json ./
EXPOSE 5050
CMD ["node", "./dist/index.js"]Kubernetes
Every service is a standard Deployment + Service pair. Example for the REST API:
apiVersion: apps/v1
kind: Deployment
metadata:
name: kb-rest-api
spec:
replicas: 2
selector:
matchLabels:
app: kb-rest-api
template:
metadata:
labels:
app: kb-rest-api
spec:
containers:
- name: rest-api
image: your-registry/kb-rest-api:1.2.0
ports:
- containerPort: 5050
env:
- name: NODE_ENV
value: production
- name: PORT
value: "5050"
- name: KB_PROJECT_ROOT
value: /app
envFrom:
- secretRef:
name: kb-labs-secrets
volumeMounts:
- name: config
mountPath: /app/.kb
readOnly: true
readinessProbe:
httpGet:
path: /api/v1/health
port: 5050
livenessProbe:
httpGet:
path: /api/v1/health
port: 5050
volumes:
- name: config
configMap:
name: kb-labs-config
---
apiVersion: v1
kind: Service
metadata:
name: kb-rest-api
spec:
selector:
app: kb-rest-api
ports:
- port: 5050
targetPort: 5050Similar Deployments for the workflow daemon, marketplace, and state daemon. The gateway is the only one that needs external exposure (via an Ingress or LoadBalancer).
Shared state
In distributed mode, services need to share state (rate limits, locks, job queues). The state daemon provides this via the State Broker HTTP API — configure your platform.core.resourceBroker.distributed: true in kb.config.json and every service points at the state daemon.
For the host registry on the gateway, use a shared Postgres or external SQLite-via-network (not ideal, but works). Without shared state, gateway restarts lose connected hosts.
Scaling
- Gateway: scale horizontally. Ensure all instances share
GATEWAY_JWT_SECRETso JWTs verify across instances. Pin host agents to specific gateway instances via sticky sessions if needed. - REST API: scale horizontally. Each instance is stateless except for the plugin registry, which is read-only after startup.
- Workflow daemon: don't scale horizontally yet. The job broker is process-local. Run one instance, vertically scaled. A multi-instance workflow daemon is on the roadmap but requires a distributed job queue.
- Marketplace: low load. One instance is plenty.
- State daemon: one instance per region. Scale vertically.
Zero-downtime rolling updates
For stateless services (gateway, REST API, marketplace):
- Deploy new version alongside the old.
- Shift traffic via the load balancer.
- Drain the old version: stop accepting new connections, wait for in-flight requests.
- Shut down.
Connected host agents reconnect during cutover with exponential backoff (see Services → Host Agent).
For the workflow daemon (stateful):
- Deploy new version.
- Stop the old daemon — in-flight runs are interrupted and marked
'interrupted'. - Start the new daemon — it picks up
'interrupted'runs from persistent state and resumes them.
The 'interrupted' state and recovery path are documented in Workflows → Retries & Error Handling.
Secrets management
Never put secrets in kb.config.json — use environment variables. In production, pull them from a secret manager:
- Kubernetes: Secrets +
envFrom(shown above). - Vault: sidecar or init container that writes secrets to a shared volume or stdout.
- AWS Secrets Manager: IAM role on the service + SDK call at startup (or via an init script that populates env vars).
- Plain systemd:
EnvironmentFile=/etc/kb-labs/secrets.envwithchmod 600on the file.
The platform itself doesn't integrate with any specific secret manager; it just reads env vars. Wrap the service startup in whatever secret-injection scheme fits your stack.
Monitoring
Every platform service emits:
- Structured logs through
platform.logger. Ship to Loki, Elasticsearch, Datadog, or whatever. - Prometheus metrics at
/metrics. Scrape with a Prometheus server. - Diagnostic events (errors, state changes) through
platform.analytics. Query via the analytics adapter's read-side.
See Operations → Observability for the full observability story.
What NOT to do
- Don't run multiple workflow daemons yet. The job broker is process-local. Multiple instances will race on the job queue and produce duplicate executions.
- Don't share SQLite files across hosts via NFS. SQLite's locking semantics break over network filesystems. For distributed state, use the state daemon or a proper database.
- Don't commit
kb.config.jsonwith secrets. Treat the config file as public — sensitive values go in env vars. - Don't use the insecure JWT default in production. The gateway warns about it at startup, but doesn't refuse to start. Set
GATEWAY_JWT_SECRETexplicitly. - Don't expose internal services directly. REST API, workflow daemon, marketplace should all be internal-only. Only the gateway is public.
What to read next
- Gateway → Self-Hosted — TLS, reverse proxies, gateway scaling.
- Operations → Observability — metrics, logs, traces.
- Operations → Security — secrets, sandbox, threat model.
- Configuration → kb.config.json — adapter and execution config.
- Services → Overview — per-service deep dives.