0
# Helm Integration
1
2
The Helm integration provides powerful capabilities for deploying Helm charts as Pulumi resources without requiring Tiller. This enables declarative management of complex applications with transformation capabilities and full Pulumi lifecycle management.
3
4
## Package Import
5
6
```typescript { .api }
7
import { helm } from "@pulumi/kubernetes";
8
import * as k8s from "@pulumi/kubernetes";
9
10
// Direct Helm imports
11
import { Chart } from "@pulumi/kubernetes/helm/v4";
12
import { Release } from "@pulumi/kubernetes/helm/v3";
13
```
14
15
## Chart (helm/v4) - Recommended
16
17
Chart represents a Helm chart deployment as a Pulumi ComponentResource. This is the recommended approach for new deployments.
18
19
```typescript { .api }
20
class Chart extends pulumi.ComponentResource {
21
constructor(name: string, args: ChartArgs, opts?: pulumi.ComponentResourceOptions)
22
23
// Method to get specific resources from the chart
24
public getResource(groupVersionKind: string, name: string, namespace?: string): pulumi.CustomResource
25
public getResourceProperty(groupVersionKind: string, name: string, property: string, namespace?: string): pulumi.Output<any>
26
}
27
28
interface ChartArgs {
29
// Chart source options
30
chart: pulumi.Input<string>; // Chart name or path (required)
31
version?: pulumi.Input<string>; // Chart version
32
repositoryOpts?: pulumi.Input<RepositoryOpts>; // Repository configuration
33
34
// Values and configuration
35
values?: pulumi.Input<{[key: string]: any}>; // Chart values
36
valueYamlFiles?: pulumi.Input<pulumi.Input<pulumi.asset.Asset | pulumi.asset.Archive>[]>; // YAML value files
37
38
// Deployment options
39
name?: pulumi.Input<string>; // Release name
40
namespace?: pulumi.Input<string>; // Target namespace
41
42
// Advanced options
43
skipCrds?: pulumi.Input<boolean>; // Skip CRD installation
44
skipAwait?: pulumi.Input<boolean>; // Skip waiting for resources to become ready
45
dependencyUpdate?: pulumi.Input<boolean>; // Update dependencies before deployment
46
devel?: pulumi.Input<boolean>; // Use development versions
47
48
// Security options
49
verify?: pulumi.Input<boolean>; // Verify chart integrity
50
keyring?: pulumi.Input<pulumi.asset.Asset | pulumi.asset.Archive>; // Public keys for verification
51
52
// Resource management
53
resourcePrefix?: pulumi.Input<string>; // Prefix for auto-generated resource names
54
postRenderer?: pulumi.Input<PostRenderer>; // Post-renderer specification
55
}
56
57
interface RepositoryOpts {
58
repo?: pulumi.Input<string>; // Repository URL
59
username?: pulumi.Input<string>; // Username for private repositories
60
password?: pulumi.Input<string>; // Password for private repositories
61
caFile?: pulumi.Input<pulumi.asset.Asset | pulumi.asset.Archive>; // CA file asset
62
certFile?: pulumi.Input<pulumi.asset.Asset | pulumi.asset.Archive>; // Certificate file asset
63
keyFile?: pulumi.Input<pulumi.asset.Asset | pulumi.asset.Archive>; // Key file asset
64
}
65
```
66
67
### Chart Usage Examples
68
69
```typescript { .api }
70
// Deploy NGINX Ingress Controller from Bitnami repository
71
const nginxIngress = new k8s.helm.v4.Chart("nginx-ingress", {
72
chart: "nginx-ingress-controller",
73
version: "9.3.12",
74
repositoryOpts: {
75
repo: "https://charts.bitnami.com/bitnami",
76
},
77
namespace: "ingress-nginx",
78
createNamespace: true,
79
values: {
80
replicaCount: 2,
81
service: {
82
type: "LoadBalancer",
83
annotations: {
84
"service.beta.kubernetes.io/aws-load-balancer-type": "nlb",
85
},
86
},
87
metrics: {
88
enabled: true,
89
serviceMonitor: {
90
enabled: true,
91
},
92
},
93
resources: {
94
requests: {
95
cpu: "100m",
96
memory: "128Mi",
97
},
98
limits: {
99
cpu: "500m",
100
memory: "512Mi",
101
},
102
},
103
},
104
});
105
106
// Deploy PostgreSQL from local chart
107
const postgresql = new k8s.helm.v4.Chart("postgresql", {
108
path: "./charts/postgresql",
109
namespace: "database",
110
createNamespace: true,
111
values: {
112
auth: {
113
postgresPassword: "secure-password",
114
database: "myapp",
115
},
116
persistence: {
117
enabled: true,
118
storageClass: "fast-ssd",
119
size: "20Gi",
120
},
121
metrics: {
122
enabled: true,
123
},
124
},
125
dependencyUpdate: true,
126
});
127
128
// Deploy with multiple values files
129
const monitoring = new k8s.helm.v4.Chart("prometheus", {
130
chart: "kube-prometheus-stack",
131
version: "45.7.1",
132
repositoryOpts: {
133
repo: "https://prometheus-community.github.io/helm-charts",
134
},
135
namespace: "monitoring",
136
createNamespace: true,
137
valuesFiles: [
138
"./helm-values/prometheus-base.yaml",
139
"./helm-values/prometheus-prod.yaml",
140
],
141
values: {
142
// Override specific values
143
grafana: {
144
adminPassword: "admin-secret",
145
persistence: {
146
enabled: true,
147
size: "10Gi",
148
},
149
},
150
prometheus: {
151
prometheusSpec: {
152
retention: "30d",
153
storageSpec: {
154
volumeClaimTemplate: {
155
spec: {
156
storageClassName: "fast-ssd",
157
accessModes: ["ReadWriteOnce"],
158
resources: {
159
requests: {
160
storage: "50Gi",
161
},
162
},
163
},
164
},
165
},
166
},
167
},
168
},
169
timeout: 600, // 10 minutes
170
atomic: true,
171
});
172
173
// Deploy with resource transformations
174
const app = new k8s.helm.v4.Chart("my-app", {
175
chart: "my-app",
176
repositoryOpts: {
177
repo: "https://charts.example.com",
178
},
179
values: {
180
image: {
181
tag: "v1.2.3",
182
},
183
},
184
transformations: [
185
// Add common labels to all resources
186
(args: any) => {
187
if (args.type.startsWith("kubernetes:")) {
188
args.props = pulumi.output(args.props).apply((props: any) => ({
189
...props,
190
metadata: {
191
...props.metadata,
192
labels: {
193
...props.metadata?.labels,
194
"app.kubernetes.io/managed-by": "pulumi",
195
"environment": "production",
196
},
197
},
198
}));
199
}
200
return {
201
props: args.props,
202
opts: args.opts,
203
};
204
},
205
// Modify resource requirements
206
(args: any) => {
207
if (args.type === "kubernetes:apps/v1:Deployment") {
208
args.props = pulumi.output(args.props).apply((props: any) => ({
209
...props,
210
spec: {
211
...props.spec,
212
template: {
213
...props.spec.template,
214
spec: {
215
...props.spec.template.spec,
216
containers: props.spec.template.spec.containers?.map((container: any) => ({
217
...container,
218
resources: {
219
requests: {
220
cpu: "100m",
221
memory: "128Mi",
222
},
223
limits: {
224
cpu: "500m",
225
memory: "512Mi",
226
},
227
},
228
})),
229
},
230
},
231
},
232
}));
233
}
234
return {
235
props: args.props,
236
opts: args.opts,
237
};
238
},
239
],
240
});
241
242
// Private repository with authentication
243
const privateChart = new k8s.helm.v4.Chart("private-app", {
244
chart: "private-app",
245
version: "1.0.0",
246
repositoryOpts: {
247
repo: "https://private-charts.company.com",
248
username: "chart-user",
249
password: "chart-password",
250
},
251
namespace: "production",
252
values: {
253
image: {
254
pullSecrets: ["private-registry-secret"],
255
},
256
},
257
});
258
```
259
260
### Accessing Chart Resources
261
262
```typescript { .api }
263
// Access specific resources from deployed chart
264
const nginxDeployment = nginxIngress.getResource("apps/v1/Deployment", "nginx-ingress-controller");
265
const nginxService = nginxIngress.getResource("v1/Service", "nginx-ingress-controller");
266
267
// Get specific properties from resources
268
const serviceIP = nginxIngress.getResourceProperty("v1/Service", "nginx-ingress-controller", "status");
269
270
// Export important values
271
export const ingressControllerIP = nginxService.status.loadBalancer.ingress[0].ip;
272
export const prometheusURL = monitoring.getResourceProperty("v1/Service", "prometheus-server", "spec").apply(spec =>
273
`http://${spec.clusterIP}:${spec.ports[0].port}`
274
);
275
```
276
277
## Release (helm/v3)
278
279
Release represents a Helm release using Helm v3 APIs. This provides compatibility with existing Helm workflows.
280
281
```typescript { .api }
282
class Release extends pulumi.CustomResource {
283
constructor(name: string, args: ReleaseArgs, opts?: pulumi.ResourceOptions)
284
285
public static get(name: string, id: pulumi.Input<pulumi.ID>, opts?: pulumi.CustomResourceOptions): Release
286
287
// Output properties
288
public readonly status!: pulumi.Output<string>;
289
public readonly version!: pulumi.Output<number>;
290
public readonly namespace!: pulumi.Output<string>;
291
public readonly values!: pulumi.Output<{[key: string]: any}>;
292
public readonly chart!: pulumi.Output<string>;
293
public readonly manifest!: pulumi.Output<string>;
294
}
295
296
interface ReleaseArgs {
297
// Chart source
298
chart?: pulumi.Input<string>; // Chart name or path
299
version?: pulumi.Input<string>; // Chart version
300
repositoryOpts?: pulumi.Input<RepositoryOpts>; // Repository configuration
301
302
// Release configuration
303
name?: pulumi.Input<string>; // Release name (defaults to resource name)
304
namespace?: pulumi.Input<string>; // Target namespace
305
createNamespace?: pulumi.Input<boolean>; // Create namespace if needed
306
307
// Values
308
values?: pulumi.Input<{[key: string]: any}>; // Release values
309
valuesFiles?: pulumi.Input<string[]>; // Values files
310
311
// Deployment options
312
timeout?: pulumi.Input<number>; // Timeout in seconds
313
atomic?: pulumi.Input<boolean>; // Atomic deployment
314
cleanupOnFail?: pulumi.Input<boolean>; // Cleanup on failure
315
dependencyUpdate?: pulumi.Input<boolean>; // Update dependencies
316
description?: pulumi.Input<string>; // Release description
317
devel?: pulumi.Input<boolean>; // Use development versions
318
disableOpenapiValidation?: pulumi.Input<boolean>; // Skip OpenAPI validation
319
disableWebhooks?: pulumi.Input<boolean>; // Disable admission webhooks
320
forceUpdate?: pulumi.Input<boolean>; // Force update
321
lint?: pulumi.Input<boolean>; // Lint chart before deployment
322
maxHistory?: pulumi.Input<number>; // Maximum revision history
323
recreatePods?: pulumi.Input<boolean>; // Recreate pods
324
renderSubchartNotes?: pulumi.Input<boolean>; // Render subchart notes
325
replace?: pulumi.Input<boolean>; // Replace existing release
326
resetValues?: pulumi.Input<boolean>; // Reset values to defaults
327
reuseValues?: pulumi.Input<boolean>; // Reuse previous release values
328
skipCrds?: pulumi.Input<boolean>; // Skip CRDs
329
skipTests?: pulumi.Input<boolean>; // Skip tests
330
verify?: pulumi.Input<boolean>; // Verify chart
331
waitForJobs?: pulumi.Input<boolean>; // Wait for jobs to complete
332
}
333
```
334
335
### Release Usage Examples
336
337
```typescript { .api }
338
// Basic release deployment
339
const nginxRelease = new k8s.helm.v3.Release("nginx", {
340
chart: "nginx",
341
repositoryOpts: {
342
repo: "https://charts.bitnami.com/bitnami",
343
},
344
version: "13.2.12",
345
namespace: "web",
346
createNamespace: true,
347
values: {
348
replicaCount: 3,
349
service: {
350
type: "LoadBalancer",
351
},
352
},
353
});
354
355
// Release with comprehensive configuration
356
const redisRelease = new k8s.helm.v3.Release("redis-cluster", {
357
chart: "redis",
358
repositoryOpts: {
359
repo: "https://charts.bitnami.com/bitnami",
360
},
361
version: "17.3.7",
362
namespace: "cache",
363
createNamespace: true,
364
values: {
365
architecture: "replication",
366
auth: {
367
enabled: true,
368
password: "redis-password",
369
},
370
master: {
371
persistence: {
372
enabled: true,
373
size: "8Gi",
374
storageClass: "fast-ssd",
375
},
376
},
377
replica: {
378
replicaCount: 2,
379
persistence: {
380
enabled: true,
381
size: "8Gi",
382
storageClass: "fast-ssd",
383
},
384
},
385
metrics: {
386
enabled: true,
387
serviceMonitor: {
388
enabled: true,
389
},
390
},
391
},
392
timeout: 600,
393
atomic: true,
394
cleanupOnFail: true,
395
dependencyUpdate: true,
396
maxHistory: 5,
397
waitForJobs: true,
398
});
399
400
// Release from local chart
401
const customAppRelease = new k8s.helm.v3.Release("custom-app", {
402
chart: "./charts/custom-app",
403
namespace: "applications",
404
createNamespace: true,
405
valuesFiles: [
406
"./values/base.yaml",
407
"./values/production.yaml",
408
],
409
values: {
410
image: {
411
tag: "v2.1.0",
412
},
413
ingress: {
414
enabled: true,
415
hosts: ["app.example.com"],
416
tls: [{
417
secretName: "app-tls",
418
hosts: ["app.example.com"],
419
}],
420
},
421
},
422
dependencyUpdate: true,
423
lint: true,
424
});
425
```
426
427
## Advanced Helm Patterns
428
429
### Multi-Chart Application
430
431
```typescript { .api }
432
// Database layer
433
const database = new k8s.helm.v4.Chart("database", {
434
chart: "postgresql",
435
repositoryOpts: {
436
repo: "https://charts.bitnami.com/bitnami",
437
},
438
namespace: "data",
439
createNamespace: true,
440
values: {
441
auth: {
442
database: "appdb",
443
},
444
persistence: {
445
size: "20Gi",
446
storageClass: "database-ssd",
447
},
448
},
449
});
450
451
// Cache layer
452
const cache = new k8s.helm.v4.Chart("redis", {
453
chart: "redis",
454
repositoryOpts: {
455
repo: "https://charts.bitnami.com/bitnami",
456
},
457
namespace: "cache",
458
createNamespace: true,
459
values: {
460
architecture: "standalone",
461
master: {
462
persistence: {
463
size: "5Gi",
464
},
465
},
466
},
467
});
468
469
// Application layer (depends on database and cache)
470
const application = new k8s.helm.v4.Chart("app", {
471
chart: "./charts/myapp",
472
namespace: "application",
473
createNamespace: true,
474
values: {
475
database: {
476
host: database.getResourceProperty("v1/Service", "postgresql", "metadata").name,
477
port: 5432,
478
},
479
redis: {
480
host: cache.getResourceProperty("v1/Service", "redis-master", "metadata").name,
481
port: 6379,
482
},
483
},
484
}, {
485
dependsOn: [database, cache],
486
});
487
```
488
489
### Chart with Custom Resources
490
491
```typescript { .api }
492
// Deploy operator first
493
const operator = new k8s.helm.v4.Chart("my-operator", {
494
chart: "my-operator",
495
repositoryOpts: {
496
repo: "https://charts.example.com",
497
},
498
namespace: "operators",
499
createNamespace: true,
500
values: {
501
image: {
502
tag: "v1.0.0",
503
},
504
resources: {
505
limits: {
506
cpu: "200m",
507
memory: "256Mi",
508
},
509
},
510
},
511
timeout: 300,
512
});
513
514
// Deploy application using custom resources
515
const customResourceApp = new k8s.helm.v4.Chart("custom-app", {
516
chart: "custom-app",
517
repositoryOpts: {
518
repo: "https://charts.example.com",
519
},
520
namespace: "applications",
521
createNamespace: true,
522
values: {
523
customResource: {
524
enabled: true,
525
spec: {
526
replicas: 3,
527
version: "v2.0.0",
528
},
529
},
530
},
531
}, {
532
dependsOn: [operator],
533
});
534
```
535
536
### Environment-Specific Configurations
537
538
```typescript { .api }
539
// Base configuration
540
const baseValues = {
541
replicaCount: 1,
542
image: {
543
repository: "myapp",
544
tag: "latest",
545
pullPolicy: "Always",
546
},
547
service: {
548
type: "ClusterIP",
549
port: 80,
550
},
551
resources: {
552
requests: {
553
cpu: "100m",
554
memory: "128Mi",
555
},
556
},
557
};
558
559
// Development environment
560
const devValues = {
561
...baseValues,
562
replicaCount: 1,
563
image: {
564
...baseValues.image,
565
tag: "dev",
566
},
567
ingress: {
568
enabled: true,
569
hosts: ["dev-app.example.com"],
570
},
571
};
572
573
// Production environment
574
const prodValues = {
575
...baseValues,
576
replicaCount: 3,
577
image: {
578
...baseValues.image,
579
tag: "v1.0.0",
580
pullPolicy: "IfNotPresent",
581
},
582
service: {
583
...baseValues.service,
584
type: "LoadBalancer",
585
},
586
resources: {
587
requests: {
588
cpu: "200m",
589
memory: "256Mi",
590
},
591
limits: {
592
cpu: "500m",
593
memory: "512Mi",
594
},
595
},
596
ingress: {
597
enabled: true,
598
hosts: ["app.example.com"],
599
tls: [{
600
secretName: "app-tls",
601
hosts: ["app.example.com"],
602
}],
603
},
604
autoscaling: {
605
enabled: true,
606
minReplicas: 3,
607
maxReplicas: 10,
608
targetCPUUtilizationPercentage: 70,
609
},
610
};
611
612
// Deploy based on environment
613
const environment = pulumi.getStack();
614
const values = environment === "production" ? prodValues : devValues;
615
616
const app = new k8s.helm.v4.Chart("myapp", {
617
chart: "myapp",
618
repositoryOpts: {
619
repo: "https://charts.example.com",
620
},
621
namespace: `myapp-${environment}`,
622
createNamespace: true,
623
values: values,
624
});
625
```
626
627
## Best Practices
628
629
### Chart Management Best Practices
630
631
1. **Use Chart v4**: Prefer `helm.v4.Chart` over `helm.v3.Release` for new deployments
632
2. **Version Pinning**: Always specify chart versions for production deployments
633
3. **Values Organization**: Use separate values files for different environments
634
4. **Resource Limits**: Always set resource requests and limits
635
5. **Transformations**: Use transformations for cross-cutting concerns like labels and annotations
636
637
### Security Best Practices
638
639
1. **Private Repositories**: Use credentials for private chart repositories
640
2. **Values Encryption**: Encrypt sensitive values using Pulumi secrets
641
3. **RBAC**: Ensure proper service account and RBAC configurations
642
4. **Image Security**: Use specific image tags and trusted registries
643
5. **Network Policies**: Apply network policies to restrict traffic
644
645
### Operations Best Practices
646
647
1. **Atomic Deployments**: Use atomic deployments for production
648
2. **Timeouts**: Set appropriate timeouts for chart deployments
649
3. **Dependencies**: Properly manage chart dependencies and update them
650
4. **Monitoring**: Monitor chart deployments and resource health
651
5. **Rollback Strategy**: Plan for rollback scenarios and test rollback procedures
652
653
The Helm integration provides powerful capabilities for managing complex Kubernetes applications while maintaining the benefits of Pulumi's infrastructure-as-code approach, enabling reliable and repeatable deployments across environments.