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
/readyzreturns: a 200 or a 503, with each check reported only asokorunavailable. 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/readyzand exits 0 or 1. Compose health checks use it. - Keep
HEALTH_ADDRinternal: 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-initcontainer writes a dev CA, the gateway certificate, client certificates fordemo-agentandmy-agent, and a random UI login intodeployments/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 withCONDUIT_UI_PORTandCONDUIT_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 hasdemo=true) sends a run every 8 seconds. Start without it usingCONDUIT_DEMO_AGENT=0, or stop it withdocker compose -p conduit-quickstart stop demo-agent. - Version: images default to
v0.2.0; setCONDUIT_VERSIONto 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 migratewith 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 SPANSso it’s recreated with the new settings. - UI auth:
UI_API_TOKENis gone. ConfigureUI_AUTH_MODEand its settings (see security.md). - Tempo endpoint:
TEMPO_ENDPOINTmust point at Tempo’s OTLP/HTTP port,:4318. The collector posts to/v1/traces. Port:4317is 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. |