Before you start
- Helm 3.8+ (OCI registry support) — or skip to the raw manifests.
- A
ReadWriteOnceblock-storage StorageClass (EBS, GCE PD, Azure Disk, Ceph RBD, local-path). Never network filesystems (NFS, EFS, Azure Files) — Conduit’s embedded database is not safe on them. - An Ingress controller or LoadBalancer terminating TLS. AI clients require HTTPS for OAuth.
Secrets first
Conduit encrypts stored secrets with a key you provide. Create it as a Kubernetes Secret before installing, and keep a copy somewhere safe (a password manager or secret manager) — stored secrets are unrecoverable without it:existingSecret.name) and never
generates one itself: a template-generated key would silently regenerate
under GitOps tooling that renders without cluster access, losing every
stored secret. A Secret also keeps the key out of data-volume snapshots, so
a leaked backup alone can’t decrypt anything.
Install with Helm
values.yaml for the embedded tier:
Recreate strategy (the data
volume is ReadWriteOnce, so the old pod must release it before the new one
starts), a Service, and the Ingress. CONDUIT_BASE_URL defaults to
https://<ingress.host>; set baseURL if you terminate TLS somewhere the
chart can’t see.
Each chart release pins its matching vX.Y.Z image, so --version is also
how you choose the Conduit version.
Active-active on PostgreSQL
With an operator-supplied PostgreSQL 14+ (see Deployment Tiers), add the connection URL to your Secret and point the chart at its key:
The chart’s Service and Ingress carry no sticky-session settings, and none
are needed: any replica serves any request.
On a fresh PostgreSQL database Conduit refuses to start without a bootstrap
password, so set
existingSecret.adminPasswordKey on first install and
remove it (with a helm upgrade) once setup completes. Concurrent boots are
safe: migrations and startup sweeps coordinate through database locks. If
your database URL lives in a Secret managed elsewhere (e.g. written by a
PostgreSQL operator), set database.secretName too — it defaults to
existingSecret.name.
IAM authentication to RDS
On EKS with RDS or Aurora,CONDUIT_DATABASE_AUTH=rds-iam replaces the
password in the database URL with per-connection IAM tokens. The AWS side —
enabling IAM auth, the rds_iam grant, the connect policy — is on
Deployment Tiers; the chart
wires the Kubernetes half. Mount the RDS CA bundle so verify-full can
validate the server certificate:
Values reference
The chart deliberately models only what these manifests need. Every otherCONDUIT_* variable in the
configuration reference goes through extraEnv
/ extraEnvFrom, which also fit External Secrets or Vault workflows:
Run
helm show values oci://ghcr.io/pipedreamhq/charts/conduit for the full
annotated list (pod placement, security contexts, service account, PDB).
Uninstalling the release keeps the data PVC (helm.sh/resource-policy: keep) — the volume is the database, so deleting it is a deliberate kubectl
action, never a side effect of helm uninstall.
Private registries and air-gapped installs
A release is exactly two artifacts — the image and the chart, with no sidecars, init containers, or subcharts pulling images of their own — so copying both into your registry is a complete mirror:vX.Y.Z
image tag matching its own version, so a mirror that preserves tags needs no
image.tag override. If your registry re-tags, set image.tag.
Then install from your registry. image.repository carries the registry
host, so pointing at the mirrored image is the only values change — plus
pull credentials if your registry requires them:
- A registry that proxies
ghcr.ioas a pull-through cache works the same way with no copy step — setimage.repositoryto the proxied path. - If your network can’t reach
ghcr.ioat all, build the image from source and push it to your registry; everything else here applies unchanged. - With the raw manifests there is no chart to
mirror: copy the image and edit the pinned
image:line to match.
Raw manifests (without Helm)
Complete, working manifest sets for both tiers — the same shapes the chart renders.Embedded tier
Active-active on PostgreSQL
Relative to the manifests above, the Deployment becomes stateless and scales horizontally:- No PVC, no volume mount,
RollingUpdate— all state lives in PostgreSQL, so updates are zero-downtime. CONDUIT_DATABASE_URL,CONDUIT_ENCRYPTION_KEY, and (pre-setup)CONDUIT_ADMIN_PASSWORDcome from Secrets. Conduit refuses to start without the key, and without the password on a fresh database — the failure messages say why.- Readiness (
/healthz/ready) verifies database connectivity, so the Service ejects an instance that lost PostgreSQL; liveness stays process-local. Concurrent boots are safe — migrations and startup sweeps coordinate through database locks. - No session affinity. Any replica serves any request — including the answer to an in-band MCP prompt or a request cancellation, which Conduit hands to the replica holding the call — so the Service and Ingress need no sticky-session settings.
Notes (both paths)
CONDUIT_BASE_URLmust match the Ingress host — it’s what OAuth callbacks and MCP discovery hand to clients.- Set
CONDUIT_TRUSTED_PROXIESto your pod CIDR (kubectl cluster-info dump | grep -m1 cluster-cidr). Conduit only honorsX-Forwarded-Forfrom proxies you declare, and an in-cluster ingress controller is not trusted by default; without it, audit entries and per-IP rate limits collapse onto the ingress address. With the chart, set it viaextraEnv(the example above). See client IP attribution. - MCP clients hold long-lived SSE connections. If your ingress enforces
a short proxy read timeout, raise it for this host — e.g. ingress-nginx:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600". - Any L7 load balancer works with its defaults. Replicas need no session affinity (see Deployment Tiers). An AWS ALB can stay on round-robin with stickiness off; Conduit writes a keep-alive frame on every idle MCP stream at least every 30 seconds, inside the ALB’s default 60-second idle timeout.
- On AZ-bound storage (EBS and friends), use a StorageClass with
volumeBindingMode: WaitForFirstConsumerso the volume is created in the AZ the pod actually schedules to. - Prometheus metrics are opt-in:
metrics.enabled: truewith the chart, or (raw manifests)CONDUIT_METRICS_ADDR=0.0.0.0:9464plus a matchingmetricscontainer port. Scrape at the pod IP; keep the port out of the Service and Ingress. See Monitoring. - A load balancer with its own target health checks (an AWS ALB, a cloud
LB) is usually the slowest link in an embedded-tier upgrade: point its
check at
/healthz/ready, tighten the interval, and shorten any deregistration/draining delay — the old target is already dead when it deregisters, so draining buys nothing.
Validate the deployment
A green readiness probe means the pod serves traffic and can reach its database. To confirm the deployment end to end, walk the same path a user will — each step exercises a different piece of configuration:- Probes —
/healthz(process) and/healthz/ready(database connectivity) return 200 through your Service. - Sign in — open
https://<your-host>in a browser and complete first-run setup (or sign in). This proves TLS, the Ingress, and session cookies. - Connect an AI client — add
https://<your-host>/mcpto any MCP client and complete the sign-in it opens. This is the sharpest test ofCONDUIT_BASE_URL: the OAuth flow round-trips through the externally reachable address, so a mismatch fails here and nowhere earlier. The client should list Conduit’s built-in tools. - Call a tool — any built-in tool (ask the client “what session am I using?”), then confirm the call appears in the audit log and usage dashboard. That proves the whole loop: authorization, execution, recording.
- Check IP attribution — if the audit entries show your ingress or
load-balancer address as the client IP, set
CONDUIT_TRUSTED_PROXIES(see Notes).
Upgrades
Read the release’s Upgrade notes in the changelog first, then upgrade deliberately:helm upgrade --version X.Y.Z with the chart (which moves the image in
lockstep), or bump the pinned image tag with raw manifests. On the embedded
tier the Recreate strategy means an upgrade takes the seconds between pod
stop and ready — sessions and tokens live in the database, so nobody is
signed out and MCP clients reconnect on their own.
To track releases automatically, run your rollout from CI: watch the
:stable tag (or GitHub releases) and apply the new pinned version when one
appears, recording it in your deploy annotations so rollout history maps to
releases.