> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ubiquex.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy Helm Chart

> Wire infrastructure outputs directly into Helm chart values using the deploy block.

Tutorial 04 introduced the `Pending<T>` system with a database endpoint flowing into a Helm chart. The `deploy` block is where that pattern lives in practice — it's the purpose-built ubx block for Helm releases, ArgoCD applications, and Flux kustomizations. Unlike a raw `unit "kubernetes_helm_release"` block, `deploy "helm"` is aware of the Pending type system from the ground up: every value in its `values` map can be a mix of resolved inputs and pending infrastructure outputs, and ubx generates exactly the right `.apply()` chains to sequence the Helm release after its dependencies. This tutorial focuses on the `deploy "helm"` block specifically — the chart name, version, namespace, and values — and shows how pending infrastructure outputs flow into Helm values cleanly.

## What you'll learn

* The `deploy "helm"` block syntax — chart, version, namespace, values
* How `Pending<T>` infrastructure outputs wire into Helm values automatically
* How ubx sequences the Helm release after its infrastructure dependencies

***

## Why this matters

<Info>
  `deploy "helm"` is the bridge between infrastructure provisioning and application deployment. Reference a resource output directly in the `values` map and the RDS endpoint becomes a database URL in a Helm values map — no CI glue, no manual copy-paste, no drift between what Terraform knows and what Helm uses.
</Info>

***

## The source code

<CodeGroup>
  ```hcl main.iac theme={"theme":"css-variables"}
  # The RDS endpoint is Pending<string> — only known after the DB is created.
  unit "aws_rds_instance" "db" {
    engine         = "postgres"
    instance_class = "db.t3.micro"
    identifier     = "myapp-${input.env}"
  }

  # deploy "helm" wires the pending endpoint into Helm values at apply time.
  # ubx generates: db.endpoint.apply(endpoint => new helm.v3.Chart(...))
  deploy "helm" "backend" {
    chart     = "my-backend"
    version   = "3.1.0"
    namespace = "backend"
    values = {
      db_host = unit.aws_rds_instance.db.endpoint  # Pending<string>
      db_port = 5432                                 # Resolved<int>
      env     = input.env                            # Resolved<string>
    }
  }
  ```

  ```hcl inputs.iac theme={"theme":"css-variables"}
  input "env" {
    type    = "string"
    default = "dev"
  }
  ```

  ```hcl outputs.iac theme={"theme":"css-variables"}
  output "db_endpoint" {
    value = unit.aws_rds_instance.db.endpoint
  }
  ```

  ```hcl ubx.iac theme={"theme":"css-variables"}
  ubx {
    name    = "deploy-helm-chart"
    runtime = "typescript"
    stacks  = ["dev", "staging", "prod"]
  }

  backend "s3" {
    bucket = "my-ubx-state"
    region = "us-east-1"
    key    = "${stack}/deploy-helm-chart.tfstate"
  }
  ```
</CodeGroup>

***

## How it works

<Steps>
  <Step title="deploy detects Pending values in its values map">
    During the IR pass, ubx scans the `values` map for pending resource references. `db_host = unit.aws_rds_instance.db.endpoint` is `Pending<string>`. The IR records this as a pending dependency on the RDS instance.
  </Step>

  <Step title="The emitter wraps the Chart in an .apply() chain">
    Because `db_host` is pending, the entire `deploy "helm" "backend"` block must wait for `db.endpoint` to resolve. The emitter generates `db.endpoint.apply(endpoint => new helm.v3.Chart(...))`, ensuring Pulumi sequences the Helm release after the RDS instance.
  </Step>

  <Step title="Resolved values are inlined directly">
    `db_port = 5432` and `env = input.env` are both resolved — they're inlined directly into the Chart constructor without any `.apply()` wrapping. Only the pending values drive the sequencing.
  </Step>
</Steps>

***

## What ubx generates

<CodeGroup>
  ```typescript index.ts theme={"theme":"css-variables"}
  // Generated by ubx — do not edit
  import * as pulumi from "@pulumi/pulumi";
  import * as aws from "@pulumi/aws";
  import * as k8s from "@pulumi/kubernetes";

  const config = new pulumi.Config();
  const env = config.get("env") || "dev";

  const db = new aws.rds.Instance("db", {
      engine: "postgres",
      instanceClass: "db.t3.micro",
      identifier: `myapp-${env}`,
  });

  // Sequenced after db.endpoint resolves
  const backend = db.endpoint.apply(endpoint =>
      new k8s.helm.v3.Chart("backend", {
          chart: "my-backend",
          version: "3.1.0",
          namespace: "backend",
          values: {
              db_host: endpoint,   // from Pending<string>
              db_port: 5432,       // resolved
              env: env,            // resolved
          },
      })
  );

  export const dbEndpoint = db.endpoint;
  ```

  ```python __main__.py theme={"theme":"css-variables"}
  # Generated by ubx — do not edit
  import pulumi
  import pulumi_aws as aws
  import pulumi_kubernetes as k8s

  config = pulumi.Config()
  env = config.get("env") or "dev"

  db = aws.rds.Instance("db",
      engine="postgres",
      instance_class="db.t3.micro",
      identifier=f"myapp-{env}",
  )

  backend = db.endpoint.apply(lambda endpoint:
      k8s.helm.v3.Chart("backend",
          chart="my-backend",
          version="3.1.0",
          namespace="backend",
          values={"db_host": endpoint, "db_port": 5432, "env": env},
      )
  )

  pulumi.export("db_endpoint", db.endpoint)
  ```

  ```go main.go theme={"theme":"css-variables"}
  // Generated by ubx — do not edit
  package main

  import (
      "github.com/pulumi/pulumi-aws/sdk/v6/go/aws/rds"
      helmv3 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v3"
      "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
      "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
  )

  func main() {
      pulumi.Run(func(ctx *pulumi.Context) error {
          cfg := config.New(ctx, "")
          env := cfg.Get("env")
          if env == "" { env = "dev" }

          db, err := rds.NewInstance(ctx, "db", &rds.InstanceArgs{
              Engine: pulumi.String("postgres"), InstanceClass: pulumi.String("db.t3.micro"),
              Identifier: pulumi.Sprintf("myapp-%s", env),
          })
          if err != nil { return err }

          _ = db.Endpoint.ApplyT(func(endpoint string) (*helmv3.Chart, error) {
              return helmv3.NewChart(ctx, "backend", helmv3.ChartArgs{
                  Chart: pulumi.String("my-backend"), Version: pulumi.String("3.1.0"),
                  Namespace: pulumi.String("backend"),
                  Values: pulumi.Map{
                      "db_host": pulumi.String(endpoint),
                      "db_port": pulumi.Int(5432),
                      "env":     pulumi.String(env),
                  },
              })
          })

          ctx.Export("dbEndpoint", db.Endpoint)
          return nil
      })
  }
  ```
</CodeGroup>

***

## Common mistakes

<Warning>
  A `deploy "helm"` block without any pending resource references in its `values` map is not sequenced after any resource — it runs immediately in parallel with everything else. If your Helm chart depends on resources existing first (namespaces, CRDs, config maps), make sure at least one resource output reference or an explicit `depends_on` links it to those resources.
</Warning>

<Tip>
  The `version` attribute pins the Helm chart version. Omitting it uses the latest version, which can cause unexpected updates when the chart is updated upstream. Always pin chart versions in production stacks.
</Tip>

***

## Run it

```bash theme={"theme":"css-variables"}
ubx validate   # type-checks Pending<T> references
ubx plan       # requires AWS + Kubernetes credentials
ubx apply      # provisions RDS first, then deploys Helm chart
```

***

## What you learned

<Check>`deploy "helm"` wires pending infrastructure outputs into Helm values automatically</Check>
<Check>ubx sequences the Helm release after its pending dependencies via `.apply()` chains</Check>
<Check>Resolved values are inlined directly — only pending values drive sequencing</Check>

***

## Next steps

<CardGroup cols={2}>
  <Card title="sync ArgoCD application" href="/v1/tutorials/26-sync-argocd-application">
    Register a GitOps application with ArgoCD
  </Card>

  <Card title="deploy block reference" href="/v1/language/deploy">
    Full deploy block syntax
  </Card>
</CardGroup>

***

<Info>
  Full runnable example: [github.com/ubiquex/ubx-examples/25-deploy-helm-chart](https://github.com/ubiquex/ubx-examples/tree/main/25-deploy-helm-chart)
</Info>
