Deployment

Conduit runs as three binaries, gateway, collector and ui. Each is shipped as a FROM scratch image built from deployments/docker/Dockerfile with --build-arg BINARY=<name>. The binaries depend on NATS JetStream, ClickHouse, VictoriaMetrics and Tempo.

Component Port Talks to
gateway 4317 (gRPC, mTLS); 8081 health NATS
collector 8081 health NATS, ClickHouse, VictoriaMetrics, Tempo (OTLP/HTTP :4318)
ui 8080 (HTTP; /healthz and /readyz unauthenticated) ClickHouse

Health checks

Every service exposes unauthenticated health endpoints over plain HTTP:

Endpoint Meaning Gateway / collector UI
/livez (UI: /healthz) The process is up HEALTH_ADDR, default :8081 main port, 8080
/readyz Dependencies are reachable: NATS for the gateway; NATS and ClickHouse for the collector; ClickHouse for the UI HEALTH_ADDR main port
  • Why a separate port for the gateway: its gRPC port requires mTLS client certificates, which probes can’t present.
  • What /readyz returns: a 200 or a 503, with each check reported only as ok or unavailable. The underlying errors go to the service log, so the endpoint never exposes hostnames or credentials.
  • Health checks in scratch images: the images have no shell or curl, so each binary includes /app healthcheck, which calls its own /readyz and exits 0 or 1. Compose health checks use it.
  • Keep HEALTH_ADDR internal: don’t expose it outside the cluster.

Local: quickstart

make quickstart (or deployments/quickstart/quickstart.sh) runs the released images with nothing but Docker:

  • Credentials: the one-shot conduit-init container writes a dev CA, the gateway certificate, client certificates for demo-agent and my-agent, and a random UI login into deployments/quickstart/.conduit/ (gitignored). It runs as your user, so the agent keys are readable by your own code, and it keeps existing files on later runs.
  • Secrets: the script writes a random ClickHouse password and session key to deployments/quickstart/.env (gitignored, owner-only).
  • Network: only the UI (127.0.0.1:8080) and the gateway (127.0.0.1:4317) listen, on loopback. Change the ports with CONDUIT_UI_PORT and CONDUIT_GATEWAY_PORT.
  • Small machines: ClickHouse uses a low-memory profile and system log tables are off, so the stack fits a 2 GiB Docker VM. Span retention is 7 days.
  • Demo data: a simulated demo-agent (no model calls; every span has demo=true) sends a run every 8 seconds. Start without it using CONDUIT_DEMO_AGENT=0, or stop it with docker compose -p conduit-quickstart stop demo-agent.
  • Version: images default to v0.2.0; set CONDUIT_VERSION to pick another release.

Get started: until your own agent sends a trace, opening the UI lands on a Get started page (/start). It checks that the stack and the sample data are working, and shows the commands that make an agent certificate (on the command line: the UI never serves keys). It then gives SDK snippets and waits, live, for the first trace from an agent other than demo-agent, linking to it when it arrives. You can hide it from the page.

CONDUIT_FROM_SOURCE=1 quickstart.sh builds the gateway, collector and UI from your checkout instead of pulling the release, one image at a time so it fits a 2 GiB Docker VM.

quickstart.sh status reprints the login, down stops the stack, and reset deletes its data and credentials. scripts/quickstart-check.sh is the CI check: from a cold start, the first trace must be visible in the UI within 300 seconds.

The quickstart is for local evaluation only. Its CA key sits next to the certificates it signs; use real PKI (below) for anything shared.

Local: docker compose (from source)

For working on Conduit itself, this stack builds the images from your checkout.

./scripts/gen-certs.sh                         # dev CA, gateway cert, example agent cert
./scripts/gen-certs.sh agent my-agent          # more agents as needed

# UI login (see security.md for other auth modes)
printf '%s' 'a-long-dev-password' | go run ./cmd/ui hash-password \
  | sed 's/^/admin:/' > deployments/docker/ui-users

export CLICKHOUSE_PASSWORD=$(openssl rand -hex 16)
docker compose -f deployments/docker/docker-compose.yml up --build

Then open http://localhost:8080 and sign in as admin. Agents connect to localhost:4317 using certs/ca.crt and their own client cert (see sdk-go.md).

To check the whole pipeline end to end, run UI_USER=admin UI_PASSWORD=… ./scripts/smoke-test.sh with the same CLICKHOUSE_PASSWORD exported. It sends a trace with the Go SDK and checks that it reaches ClickHouse, VictoriaMetrics, Tempo and the UI. CI runs it on every push.

Compose sets UI_COOKIE_INSECURE=true because it serves plain HTTP on localhost. Never set it anywhere else. Without UI_SESSION_KEY, sessions end whenever the UI restarts.

Kubernetes

The manifests in deployments/k8s/ assume a conduit namespace, cert-manager, and NATS, ClickHouse, VictoriaMetrics and Tempo reachable at the hostnames in configmap.yaml.

1. Certificates (mtls-cert.yaml)

The file creates a root CA, conduit-ca-issuer, a gateway server cert (secret conduit-mtls-certs) and an example agent cert. The gateway and the agents all chain to that one CA. The gateway Deployment maps the secret’s tls.crt, tls.key and ca.crt onto server.crt, server.key and ca.crt.

For production, swap the bootstrap self-signed root CA for your PKI but keep the issuer name. Issue one Certificate per agent with usages: [client auth]. Its commonName becomes the agent’s identity.

2. Secrets

Create conduit-secrets in the conduit namespace:

Key Used by Value
clickhouse-dsn collector, ui clickhouse://user:pass@host:9000/observability
ui-users ui (login mode) lines of username:<output of ui hash-password>
ui-session-key ui (login mode) 32 or more random bytes, for example openssl rand -hex 32
kubectl -n conduit create secret generic conduit-secrets \
  --from-literal=clickhouse-dsn="$CLICKHOUSE_DSN" \
  --from-file=ui-users=./ui-users \
  --from-literal=ui-session-key="$(openssl rand -hex 32)"

3. Apply

kubectl apply -f deployments/k8s/configmap.yaml \
              -f deployments/k8s/mtls-cert.yaml \
              -f deployments/k8s/deployment.yaml \
              -f deployments/k8s/service.yaml

The gateway Service is a ClusterIP. Expose it through a LoadBalancer or a TCP/gRPC passthrough if agents run outside the cluster. TLS must terminate at the gateway itself, or mTLS identity is lost.

On GKE, use deployments/k8s/gke-iap.yaml instead of the ui Service to put the UI behind Identity-Aware Proxy (see security.md).

Upgrading an existing deployment

  • Schema migrations: the collector applies pending migrations at startup. To migrate before rolling out new collectors, for example in a Kubernetes Job, run /app migrate with the collector image and its environment. Never edit a shipped migration file; the collector refuses to start if a checksum changes.

  • NATS stream: the SPANS stream now uses work-queue retention, so acked spans are deleted. JetStream can’t change the retention of an existing stream. Once, with the gateway and collector stopped, run nats stream rm SPANS so it’s recreated with the new settings.
  • UI auth: UI_API_TOKEN is gone. Configure UI_AUTH_MODE and its settings (see security.md).
  • Tempo endpoint: TEMPO_ENDPOINT must point at Tempo’s OTLP/HTTP port, :4318. The collector posts to /v1/traces. Port :4317 is OTLP/gRPC and won’t work.

Configuration reference

Variable Component Default
GATEWAY_ADDR gateway :4317
TLS_CERT, TLS_KEY, TLS_CA gateway /etc/certs/server.crt, server.key, ca.crt
RATE_LIMIT_RPS gateway 100 telemetry items per second per agent
HEALTH_ADDR gateway, collector :8081 (liveness and readiness endpoints)
SPAN_RETENTION_DAYS collector 90 (1–3650). The collector reconciles the spans table’s TTL on startup; shortening it deletes data that’s now past retention.
STORE_LLM_PAYLOADS gateway true. Set false to drop prompts and completions at ingestion; model, tokens and cost are kept. An unrecognized value stops startup.
NATS_URL gateway, collector nats://localhost:4222
CLICKHOUSE_DSN collector, ui local dev DSN. Always set it explicitly. Add ?secure=true for TLS to ClickHouse.
VICTORIAMETRICS_URL collector http://localhost:8428
TEMPO_ENDPOINT collector http://localhost:4318
LISTEN_ADDR ui :8080
UI_AUTH_MODE and related ui see security.md
UI_GATEWAY_ADDR ui localhost:4317. The gateway address shown in the Get started page’s snippets (display only).
UI_DEMO_AGENT ui demo-agent. The sample agent whose traces don’t count as a user’s first trace on Get started.