Skip to main content
Deploy LangSmith to Azure with the public Terraform modules. Managing the deployment as code lets you version, review, and reproduce your LangSmith environment across subscriptions instead of clicking through the Azure Portal. The install runs in two stages:
  1. Infrastructure: Terraform provisions AKS, Postgres, Redis, Blob Storage, Key Vault, cert-manager, KEDA, and ingress.
  2. Application: Helm installs the LangSmith chart against the cluster.
After the base install, enable three optional add-ons (LangSmith Deployment, Agent Builder, and Insights and Polly) by setting flags and redeploying.

Prerequisites

Required tools

Required Azure RBAC

The identity running Terraform needs the following roles on the subscription: Owner includes both. Contributor alone is insufficient because role assignments require User Access Administrator.

Authenticate

You also need a LangSmith license key (contact sales) and either a dns_label (Azure subdomain, no DNS setup needed) or a custom langsmith_domain.

Quickstart

For a condensed cheat sheet of make targets, required variables, and common constraints, see the Azure quick reference.
For the fastest path from zero to a running LangSmith instance:
Or run steps 5 through 7 in one shot:
The following sections cover each phase in detail.

Provision infrastructure

Terraform provisions the following Azure resources:

Clone and configure

All subsequent commands run from modules/azure/. Run make help for the full target list. Generate terraform.tfvars with the interactive wizard:
The wizard runs a 10-section questionnaire covering profile, subscription, naming, networking, AKS sizing, ingress controller, DNS/TLS, backend services, Key Vault, sizing profile, and security add-ons. Each section includes explanatory context, cost estimates, and trade-offs. Re-running is safe; existing values are preselected at each prompt. Press Enter to keep them. Prefer manual editing:
Minimum required values:
In-cluster ClickHouse runs as a single pod with no replication or backups, dev/POC only. For production, use LangChain Managed ClickHouse.
Blob Storage is always required, regardless of tier. Trace payloads must go to Azure Blob, never to ClickHouse.
For all variables, see the Azure variables reference.

Bootstrap secrets

setup-env.sh writes infra/secrets.auto.tfvars (gitignored, chmod 600). Terraform picks this file up automatically; no shell exports needed.
  • First run: prompts for PostgreSQL password, LangSmith license key, admin password, and admin email. Generates api_key_salt, jwt_secret, and four Fernet encryption keys locally.
  • Subsequent runs: reads the six generated secrets (api_key_salt, jwt_secret, and the four Fernet keys) from Azure Key Vault. Re-prompts for the PostgreSQL password, license key, admin password, and admin email unless LANGSMITH_PG_PASSWORD, LANGSMITH_LICENSE_KEY, LANGSMITH_ADMIN_PASSWORD, and LANGSMITH_ADMIN_EMAIL are set in the environment.
Never commit secrets.auto.tfvars. It is gitignored. Regenerate on any machine by running make setup-env.

Preflight

Validates Azure CLI auth, the active subscription, 11 required resource providers, RBAC (Contributor + User Access Administrator), terraform.tfvars and secrets.auto.tfvars presence, and terraform/kubectl/helm on PATH.

Apply

Provisioning the Azure cloud foundation takes 15 to 20 minutes on a clean subscription. Do not interrupt the apply.
Skip make plan on a fresh deploy. kubernetes_manifest resources require a live cluster API during plan, which does not exist yet. make apply handles resource ordering in three internal stages: Azure infrastructure including AKS → Kubernetes bootstrap (namespace, secrets, cert-manager, KEDA) → ClusterIssuer and remaining manifests.

Cluster credentials and Kubernetes Secrets

After make apply completes, get cluster credentials and push secrets into the cluster:
make k8s-secrets reads 8 secrets from Key Vault and creates or updates langsmith-config-secret. Safe to re-run; uses --dry-run=client | kubectl apply to update in place.

Verify infrastructure

Deploy LangSmith

Use one of the two supported deployment paths:

Generate Helm values

make init-values reads terraform output and terraform.tfvars and generates helm/values/values-overrides.yaml with all fields populated:
  • config.hostname, your FQDN (from dns_label or langsmith_domain).
  • config.initialOrgAdminEmail, the first org admin account.
  • config.existingSecretName: langsmith-config-secret, secrets reference.
  • config.blobStorage, storage account name + container + Workload Identity client ID.
  • Workload Identity annotations for 8 ServiceAccounts (backend, platform-backend, queue, ingest-queue, host-backend, listener, agent-builder-tool-server, agent-builder-trigger-server).
  • Ingress + TLS block (cert-manager annotation, TLS secret name).
  • Postgres and Redis external secret references (when postgres_source = "external" / redis_source = "external").
Also copies the sizing overlay and any enabled add-on overlays from helm/values/examples/ into helm/values/.
The admin email is read from langsmith_admin_email in terraform.tfvars (set during make setup-env) and written into values-overrides.yaml automatically. No manual editing needed.

Deploy

make deploy does the following:
  1. Validates values-overrides.yaml exists.
  2. Refreshes kubeconfig via az aks get-credentials.
  3. Annotates the LoadBalancer service with service.beta.kubernetes.io/azure-dns-label-name, required for Azure to assign the DNS label to the public IP.
  4. Creates the letsencrypt-prod cert-manager ClusterIssuer if tls_certificate_source = "letsencrypt" (idempotent).
  5. Runs preflight checks (tools, cluster connectivity, Helm repo).
  6. Verifies langsmith-config-secret exists; auto-creates from Key Vault if it is missing.
  7. Builds and logs the values chain.
  8. Auto-recovers any stuck pending-upgrade Helm release before proceeding.
  9. Runs helm upgrade --install langsmith langchain/langsmith --timeout 20m.
  10. Waits for core deployments to roll out.
  11. Annotates the langsmith-ksa ServiceAccount with the Workload Identity client ID.
  12. Prints the access URL and login credentials location.
Why --timeout 20m? The langsmith-backend-auth-bootstrap Job runs DB migrations and org initialization as a post-install hook. This takes up to 5 minutes on first install. Without a long timeout, Helm may report failure even though the install eventually succeeds.
Watch pods in a second terminal:
If you completed the Helm path, skip to Verify the deployment. The following Terraform path is an alternative to the Helm path, not an additional step.

Terraform path

Use this path when you want the Helm release, Kubernetes Secrets, and Workload Identity ServiceAccount managed in Terraform state.
Feature flags in app/terraform.tfvars:
End-to-end via Terraform (infrastructure + application):

Verify the deployment

Expected pod state (all Running after ~5 minutes):
Open https://<HOSTNAME> and log in with the admin email and password from Key Vault:

Values chain

make deploy applies Helm values files in this order (last file wins on conflicts):
All files in helm/values/ are gitignored (generated or contain live secrets). Source templates live in helm/values/examples/ and are copied by make init-values.

Day-2 operations

Enable add-ons

Each add-on is gated by a flag in infra/terraform.tfvars. Set the flag, re-run make init-values to regenerate values, then re-run make deploy.

LangSmith Deployment

Enables LangSmith Deployment, which lets you deploy and manage agents as API servers directly from the LangSmith UI. This adds three new pods.

Scale the node pool first

Before enabling, bump default_node_pool_min_count to at least 5. The operator spawns agent deployment pods on demand and needs node headroom:
Without sufficient node capacity, operator-spawned agent pods stay in Pending state indefinitely. Scale the node pool first, then enable.

Apply, regenerate values, deploy

make init-values appends the LangSmith Deployment add-on overlay (langsmith-values-agent-deploys.yaml) to the values chain. It automatically injects:
config.deployment.url must include https://. Missing the protocol causes operator-deployed agents to stay stuck in DEPLOYING state indefinitely. The URL is injected automatically by make init-values. Do not set it manually in the overlay file; it is overwritten on the next run.
config.deployment.enabled: true is required. Setting only config.deployment.url without enabled: true causes the chart to silently skip creating listener and operator. No error, they never appear.

Verify

Expected: langsmith-host-backend, langsmith-listener, and langsmith-operator all Running. Total pod count: ~20 Running + 3 Completed jobs. KEDA is already installed alongside infrastructure. With enable_deployments = true, the operator creates KEDA ScaledObject resources for each agent deployment’s worker queue. Worker pods scale down to zero when idle and scale up based on Redis queue depth.

Agent Builder

Provides visual AI-assisted creation and management of LangGraph agents from the LangSmith UI. No terraform apply needed; run make init-values && make deploy. Prerequisite: LangSmith Deployment enabled (enable_deployments = true). Enabling Agent Builder without it causes a preflight error. Enable:
make init-values appends the Agent Builder add-on overlay (langsmith-values-agent-builder.yaml) to the values chain. The overlay enables the Agent Builder UI and supporting services, sets backend.agentBootstrap.enabled: true (the post-install job that registers Agent Builder as a LangSmith Deployment and creates the required ConfigMap), and sets conservative agent worker pod resources (1 to 2 CPU, 512 MiB to 1 GiB memory) instead of the chart’s default 4 to 8 GiB memory. Verify:
Expected: 3 static pods (tool-server, trigger-server, bootstrap Job) + 4 dynamic pods. Total: ~26 pods. After make deploy, an Agent Builder section appears in the LangSmith UI navigation.
Roll the frontend after agentBootstrap completes. The agentBootstrap Job creates the langsmith-polly-config ConfigMap that the frontend reads for the Polly UI. If the frontend was running when bootstrap completed, Polly shows “Unable to connect to LangGraph server”. Fix:
Encryption key is read from langsmith-config-secret. Do not set config.agentBuilder.encryptionKey inline in values-overrides.yaml. The chart reads it from langsmith-config-secret via existingSecretName. Setting it inline overrides the secret reference and creates a mismatch.
Both langsmith-agent-builder-tool-server and langsmith-agent-builder-trigger-server need Workload Identity to access Azure Blob Storage. Their federated credentials are pre-registered in modules/k8s-cluster/main.tf; no additional setup is needed.

Insights and Polly

Two features, both of which require LangSmith Deployment. They are independent of each other; enable either one without the other.
  • Insights: AI-powered trace analytics (Clio). Surfaces patterns and anomalies in LangSmith traces. Clio deploys as a dynamic LangGraph deployment through the operator on first UI invocation. Adds no new static pods.
  • Polly: AI-powered evaluation and monitoring agent. Runs as a dynamic LangGraph deployment, operator-managed. The overlay enables Polly (top-level polly.enabled: true); the operator manages its resources.
No terraform apply needed; run make init-values && make deploy.
Enable one:
make init-values appends the add-on overlays based on clickhouse_source in terraform.tfvars:
  • clickhouse_source = "in-cluster", generates a minimal overlay (top-level insights.enabled: true only). The Helm chart manages ClickHouse internally.
  • clickhouse_source = "external", generates a full overlay with clickhouse.external.enabled: true and a langsmith-clickhouse secret reference. Create this secret with the ClickHouse host and credentials before deploying.
Do not manually copy the Insights example file for in-cluster ClickHouse. The example helm/values/examples/langsmith-values-insights.yaml has clickhouse.external.enabled: true and existingSecretName: langsmith-clickhouse. Copying it manually when using in-cluster ClickHouse causes CreateContainerConfigError because the secret does not exist. Always use make init-values to generate the correct file.
Verify:
Encryption keys must never change after first enable. insights_encryption_key and polly_encryption_key must never change after first enable. Changing either permanently corrupts all existing encrypted data. There is no recovery path. These keys live in Key Vault and never rotate automatically.
Roll the frontend after first Polly enable. If the Polly UI shows “Unable to connect to LangGraph server” after enabling, the frontend started before the bootstrap ConfigMap was ready. Fix:

Add-on summary

Ingress controllers

Set ingress_controller in terraform.tfvars before make apply. For the full TLS compatibility matrix, see INGRESS_CONTROLLERS.md in the Azure module repo.
letsencrypt (HTTP-01) only works with nginx, istio (self-managed), and envoy-gateway. istio-addon does not create an IngressClass, so the ACME solver cannot receive traffic. With agic, the Application Gateway rewrites the ACME challenge path, so the HTTP-01 solver fails. For both, use dns01 with a custom domain, or none for HTTP-only.

DNS and TLS

dns_label gives you a free Azure subdomain, <label>.<region>.cloudapp.azure.com, with no domain registration or DNS zone needed. deploy.sh annotates the correct LoadBalancer service automatically. Quickstart default (HTTP, zero setup):
Add HTTPS with Let’s Encrypt (nginx, self-managed istio, or envoy-gateway):
Custom domain + DNS-01 (all controllers, works behind firewalls):
dns01 flow:
  1. make apply creates the Azure DNS zone and outputs 4 nameservers.
  2. At your registrar, add NS records for the subdomain pointing to those 4 nameservers.
  3. Verify: dig NS langsmith.mycompany.com @8.8.8.8.
  4. make deploy issues the cert via DNS-01 automatically (Workload Identity writes the TXT record to Azure DNS).
  5. Get the LB IP, add ingress_ip = "<ip>" to terraform.tfvars, then make apply (creates the A record).
  6. make status shows exactly what NS and A records to add at each stage.
Why NS records, not CNAME: cert-manager must write TXT records to the zone to prove ownership. That requires Azure DNS to be authoritative for the subdomain, and NS delegation grants that authority. A CNAME only aliases traffic and does not transfer DNS authority; the DNS-01 challenge fails.

Next steps