The kubernetes:helm.sh/v4:Chart resource, part of the Pulumi Kubernetes provider, renders Helm chart templates and manages the resulting Kubernetes resources directly through Pulumi. This guide focuses on three capabilities: chart sources (local directories, repositories, OCI registries), values customization, and namespace placement.
Charts require a configured Kubernetes cluster and may reference Helm repositories or local chart directories. The Chart resource does not use Tiller or create a Helm Release; it renders templates (equivalent to helm template --dry-run=server) and deploys the resulting manifests. The examples are intentionally small. Combine them with your own values files, namespaces, and cluster configuration.
Deploy a chart from a local directory
Teams developing or customizing charts often work with unpacked directories on disk, allowing iteration on templates before publishing.
import * as k8s from "@pulumi/kubernetes";
const nginx = new k8s.helm.v4.Chart("nginx", {
chart: "./nginx",
});
import pulumi
from pulumi_kubernetes.helm.v4 import Chart
nginx = Chart("nginx",
chart="./nginx"
)
package main
import (
helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{
Chart: pulumi.String("./nginx"),
})
if err != nil {
return err
}
return nil
})
}
using Pulumi;
using Pulumi.Kubernetes.Types.Inputs.Helm.V4;
using System.Collections.Generic;
return await Deployment.RunAsync(() =>
{
new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs
{
Chart = "./nginx"
});
return new Dictionary<string, object?>{};
});
package generated_program;
import com.pulumi.Pulumi;
import com.pulumi.kubernetes.helm.v4.Chart;
import com.pulumi.kubernetes.helm.v4.ChartArgs;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var nginx = new Chart("nginx", ChartArgs.builder()
.chart("./nginx")
.build());
});
}
}
name: example
runtime: yaml
resources:
nginx:
type: kubernetes:helm.sh/v4:Chart
properties:
chart: ./nginx
The chart property accepts a path to a local directory containing a valid Helm chart. If a Chart.lock file exists and dependencies are missing, Pulumi automatically rebuilds them. This approach works well during development when you’re modifying chart templates directly.
Pull a chart from a Helm repository
Production deployments typically fetch charts from public or private repositories, ensuring consistent versions across environments.
import * as k8s from "@pulumi/kubernetes";
const nginx = new k8s.helm.v4.Chart("nginx", {
chart: "nginx",
repositoryOpts: {
repo: "https://charts.bitnami.com/bitnami",
},
});
import pulumi
from pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs
nginx = Chart("nginx",
chart="nginx",
repository_opts=RepositoryOptsArgs(
repo="https://charts.bitnami.com/bitnami",
)
)
package main
import (
helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{
Chart: pulumi.String("nginx"),
RepositoryOpts: &helmv4.RepositoryOptsArgs{
Repo: pulumi.String("https://charts.bitnami.com/bitnami"),
},
})
if err != nil {
return err
}
return nil
})
}
using Pulumi;
using Pulumi.Kubernetes.Types.Inputs.Helm.V4;
using System.Collections.Generic;
return await Deployment.RunAsync(() =>
{
new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs
{
Chart = "nginx",
RepositoryOpts = new RepositoryOptsArgs
{
Repo = "https://charts.bitnami.com/bitnami"
},
});
return new Dictionary<string, object?>{};
});
package generated_program;
import com.pulumi.Pulumi;
import com.pulumi.kubernetes.helm.v4.Chart;
import com.pulumi.kubernetes.helm.v4.ChartArgs;
import com.pulumi.kubernetes.helm.v4.inputs.RepositoryOptsArgs;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var nginx = new Chart("nginx", ChartArgs.builder()
.chart("nginx")
.repositoryOpts(RepositoryOptsArgs.builder()
.repo("https://charts.bitnami.com/bitnami")
.build())
.build());
});
}
}
name: example
runtime: yaml
resources:
nginx:
type: kubernetes:helm.sh/v4:Chart
properties:
chart: nginx
repositoryOpts:
repo: https://charts.bitnami.com/bitnami
The repositoryOpts property specifies the Helm repository URL. Pulumi fetches the latest stable version unless you specify a version. This example pulls the nginx chart from Bitnami’s public repository.
Pull a chart from an OCI registry
OCI registries provide an alternative distribution method, using the same infrastructure as container images.
import * as k8s from "@pulumi/kubernetes";
const nginx = new k8s.helm.v4.Chart("nginx", {
chart: "oci://registry-1.docker.io/bitnamicharts/nginx",
version: "16.0.7",
});
import pulumi
from pulumi_kubernetes.helm.v4 import Chart
nginx = Chart("nginx",
chart="oci://registry-1.docker.io/bitnamicharts/nginx",
version="16.0.7",
)
package main
import (
helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{
Chart: pulumi.String("oci://registry-1.docker.io/bitnamicharts/nginx"),
Version: pulumi.String("16.0.7"),
})
if err != nil {
return err
}
return nil
})
}
using Pulumi;
using Pulumi.Kubernetes.Types.Inputs.Helm.V4;
using System.Collections.Generic;
return await Deployment.RunAsync(() =>
{
new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs
{
Chart = "oci://registry-1.docker.io/bitnamicharts/nginx",
Version = "16.0.7",
});
return new Dictionary<string, object?>{};
});
package generated_program;
import com.pulumi.Pulumi;
import com.pulumi.kubernetes.helm.v4.Chart;
import com.pulumi.kubernetes.helm.v4.ChartArgs;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var nginx = new Chart("nginx", ChartArgs.builder()
.chart("oci://registry-1.docker.io/bitnamicharts/nginx")
.version("16.0.7")
.build());
});
}
}
name: example
runtime: yaml
resources:
nginx:
type: kubernetes:helm.sh/v4:Chart
properties:
chart: oci://registry-1.docker.io/bitnamicharts/nginx
version: "16.0.7"
The chart property accepts an oci:// URL pointing to a chart in an OCI-compatible registry. The version property pins the chart to a specific release. OCI charts require explicit version specification.
Override chart values with files and maps
Charts expose configuration through values that control resource sizing, service types, and application behavior.
import * as pulumi from "@pulumi/pulumi";
import * as k8s from "@pulumi/kubernetes";
const nginx = new k8s.helm.v4.Chart("nginx", {
chart: "nginx",
repositoryOpts: {
repo: "https://charts.bitnami.com/bitnami",
},
valueYamlFiles: [
new pulumi.asset.FileAsset("./values.yaml")
],
values: {
service: {
type: "ClusterIP",
},
notes: new pulumi.asset.FileAsset("./notes.txt"),
},
});
"""A Kubernetes Python Pulumi program"""
import pulumi
from pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs
nginx = Chart("nginx",
chart="nginx",
repository_opts=RepositoryOptsArgs(
repo="https://charts.bitnami.com/bitnami"
),
value_yaml_files=[
pulumi.FileAsset("./values.yaml")
],
values={
"service": {
"type": "ClusterIP"
},
"notes": pulumi.FileAsset("./notes.txt")
}
)
package main
import (
helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{
Chart: pulumi.String("nginx"),
RepositoryOpts: &helmv4.RepositoryOptsArgs{
Repo: pulumi.String("https://charts.bitnami.com/bitnami"),
},
ValueYamlFiles: pulumi.AssetOrArchiveArray{
pulumi.NewFileAsset("./values.yaml"),
},
Values: pulumi.Map{
"service": pulumi.Map{
"type": pulumi.String("ClusterIP"),
},
"notes": pulumi.NewFileAsset("./notes.txt"),
},
})
if err != nil {
return err
}
return nil
})
}
using Pulumi;
using Pulumi.Kubernetes.Types.Inputs.Helm.V4;
using System.Collections.Generic;
return await Deployment.RunAsync(() =>
{
new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs
{
Chart = "nginx",
RepositoryOpts = new RepositoryOptsArgs
{
Repo = "https://charts.bitnami.com/bitnami"
},
ValueYamlFiles =
{
new FileAsset("./values.yaml")
},
Values = new InputMap<object>
{
["service"] = new InputMap<object>
{
["type"] = "ClusterIP",
},
["notes"] = new FileAsset("./notes.txt")
},
});
return new Dictionary<string, object?>{};
});
package generated_program;
import java.util.Map;
import com.pulumi.Pulumi;
import com.pulumi.kubernetes.helm.v4.Chart;
import com.pulumi.kubernetes.helm.v4.ChartArgs;
import com.pulumi.kubernetes.helm.v4.inputs.RepositoryOptsArgs;
import com.pulumi.asset.FileAsset;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var nginx = new Chart("nginx", ChartArgs.builder()
.chart("nginx")
.repositoryOpts(RepositoryOptsArgs.builder()
.repo("https://charts.bitnami.com/bitnami")
.build())
.valueYamlFiles(new FileAsset("./values.yaml"))
.values(Map.of(
"service", Map.of(
"type", "ClusterIP"),
"notes", new FileAsset("./notes.txt")))
.build());
});
}
}
name: example
runtime: yaml
resources:
nginx:
type: kubernetes:helm.sh/v4:Chart
properties:
chart: nginx
repositoryOpts:
repo: https://charts.bitnami.com/bitnami
valueYamlFiles:
- fn::fileAsset: values.yaml
values:
service:
type: ClusterIP
notes:
fn::fileAsset: notes.txt
The valueYamlFiles property accepts Pulumi FileAssets that are read and merged with the chart’s default values. The values property provides inline overrides with highest precedence. You can use nested maps, Pulumi outputs, and FileAssets as values. Assets are automatically opened and converted to strings.
Deploy a chart into a specific namespace
Kubernetes namespaces isolate resources by environment or team. Charts need explicit namespace configuration to deploy outside the default.
import * as pulumi from "@pulumi/pulumi";
import * as k8s from "@pulumi/kubernetes";
const ns = new k8s.core.v1.Namespace("nginx", {
metadata: { name: "nginx" },
});
const nginx = new k8s.helm.v4.Chart("nginx", {
namespace: ns.metadata.name,
chart: "nginx",
repositoryOpts: {
repo: "https://charts.bitnami.com/bitnami",
}
});
import pulumi
from pulumi_kubernetes.meta.v1 import ObjectMetaArgs
from pulumi_kubernetes.core.v1 import Namespace
from pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs
ns = Namespace("nginx",
metadata=ObjectMetaArgs(
name="nginx",
)
)
nginx = Chart("nginx",
namespace=ns.metadata.name,
chart="nginx",
repository_opts=RepositoryOptsArgs(
repo="https://charts.bitnami.com/bitnami",
)
)
package main
import (
corev1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1"
helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4"
metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
ns, err := corev1.NewNamespace(ctx, "nginx", &corev1.NamespaceArgs{
Metadata: &metav1.ObjectMetaArgs{Name: pulumi.String("nginx")},
})
if err != nil {
return err
}
_, err = helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{
Namespace: ns.Metadata.Name(),
Chart: pulumi.String("nginx"),
RepositoryOpts: &helmv4.RepositoryOptsArgs{
Repo: pulumi.String("https://charts.bitnami.com/bitnami"),
},
})
if err != nil {
return err
}
return nil
})
}
using Pulumi;
using Pulumi.Kubernetes.Types.Inputs.Core.V1;
using Pulumi.Kubernetes.Types.Inputs.Meta.V1;
using Pulumi.Kubernetes.Types.Inputs.Helm.V4;
using System.Collections.Generic;
return await Deployment.RunAsync(() =>
{
var ns = new Pulumi.Kubernetes.Core.V1.Namespace("nginx", new NamespaceArgs
{
Metadata = new ObjectMetaArgs{Name = "nginx"}
});
new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs
{
Namespace = ns.Metadata.Apply(m => m.Name),
Chart = "nginx",
RepositoryOpts = new RepositoryOptsArgs
{
Repo = "https://charts.bitnami.com/bitnami"
},
});
return new Dictionary<string, object?>{};
});
package generated_program;
import com.pulumi.Pulumi;
import com.pulumi.kubernetes.core.v1.Namespace;
import com.pulumi.kubernetes.core.v1.NamespaceArgs;
import com.pulumi.kubernetes.helm.v4.Chart;
import com.pulumi.kubernetes.helm.v4.ChartArgs;
import com.pulumi.kubernetes.helm.v4.inputs.RepositoryOptsArgs;
import com.pulumi.kubernetes.meta.v1.inputs.ObjectMetaArgs;
import com.pulumi.core.Output;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var ns = new Namespace("nginx", NamespaceArgs.builder()
.metadata(ObjectMetaArgs.builder()
.name("nginx")
.build())
.build());
var nginx = new Chart("nginx", ChartArgs.builder()
.namespace(ns.metadata().apply(m -> Output.of(m.name().get())))
.chart("nginx")
.repositoryOpts(RepositoryOptsArgs.builder()
.repo("https://charts.bitnami.com/bitnami")
.build())
.build());
});
}
}
name: example
runtime: yaml
resources:
ns:
type: kubernetes:core/v1:Namespace
properties:
metadata:
name: nginx
nginx:
type: kubernetes:helm.sh/v4:Chart
properties:
namespace: ${ns.metadata.name}
chart: nginx
repositoryOpts:
repo: https://charts.bitnami.com/bitnami
The namespace property controls where chart resources are created. This example creates a Namespace resource first, then references its name when deploying the chart. Pulumi applies a default namespace based on the namespace input, the provider’s configured namespace, and the active Kubernetes context.
Beyond these examples
These snippets focus on specific Chart-level features: chart sources (local, repository, OCI), values customization, and namespace placement. They’re intentionally minimal rather than full application deployments.
The examples assume pre-existing infrastructure such as a Kubernetes cluster with configured kubeconfig, and Helm repositories added to local configuration (for chart references with repo prefix). They focus on configuring the Chart rather than provisioning the cluster or managing Helm repositories.
To keep things focused, common Chart patterns are omitted, including:
- CRD installation control (skipCrds)
- Resource readiness waiting (skipAwait)
- Chart verification and signing (verify, keyring)
- Dependency management (dependencyUpdate)
- Post-rendering transformations (postRenderer)
- Resource name prefixing (resourcePrefix)
These omissions are intentional: the goal is to illustrate how each Chart feature is wired, not provide drop-in Kubernetes modules. See the Helm Chart resource reference for all available configuration options.
Let's deploy Kubernetes Applications with Helm Charts
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Chart Sources & Resolution
example/mariadb), (2) path to packaged chart (./nginx-1.2.3.tgz), (3) path to unpacked directory (./nginx), (4) absolute URL (https://example.com/charts/nginx-1.2.3.tgz), (5) chart reference with repositoryOpts (chart: "nginx" with repo URL), or (6) OCI registry (oci://example.com/charts/nginx with version).chart to an OCI URL (e.g., oci://registry-1.docker.io/bitnamicharts/nginx) and specify the version property.devel: true to include development versions (alpha, beta, release candidates), or specify an exact version.Values & Configuration
valueYamlFiles for values files (as Pulumi Assets) and values for a map of chart values. The values input has highest precedence and supports literals, nested maps, Pulumi outputs, and assets.--set service.type aren’t supported. Use the values input with nested maps instead.namespace property. The namespace is determined by this input, the provider’s configured namespace, and the active Kubernetes context.Resource Management & Ordering
helm template --dry-run=server) and manages resources directly with Pulumi. It doesn’t use Tiller or create a Helm Release.skipAwait is enabled. Use the config.kubernetes.io/depends-on annotation to declare explicit dependencies.config.kubernetes.io/depends-on annotation only supports references to resources within the same Chart.skipCrds: true to skip installing CRDs from the chart’s crds/ directory.Dependencies & Chart Management
Chart.lock file exists. Set dependencyUpdate: true to update dependencies before installation.