Helm Integration
Deploy applications using Helm charts with TypeKro's type-safe configuration and smart value templating. This approach combines the rich Helm ecosystem with TypeKro's magic proxy system for schema references and CEL expressions.
Quick Start
Basic Helm Release
Deploy a Helm chart with static configuration:
import { toResourceGraph, helmRelease } from 'typekro';
import { type } from 'arktype';
const AppSchema = type({
name: 'string',
version: 'string',
replicas: 'number'
});
const appGraph = toResourceGraph(
{
name: 'nginx-app',
apiVersion: 'example.com/v1alpha1',
kind: 'NginxApp',
spec: AppSchema
},
(schema) => {
// Create repository first
const repository = helmRepository({
name: 'bitnami',
url: 'https://charts.bitnami.com/bitnami',
interval: '10m'
});
// Create app using simple factory
const app = simpleHelmChart(
'nginx',
repository.spec.url, // Reference repository URL by field
'nginx',
{
replicaCount: schema.spec.replicas,
image: {
tag: schema.spec.version
}
}
);
return { repository, app };
}
);
Deployment
// Generate Kro YAML
const yaml = appGraph.toYaml();
console.log(yaml);
// Deploy with factory
const factory = await appGraph.factory('kro');
await factory.deploy({
name: 'my-nginx',
version: '1.21.6',
replicas: 3
});
Core Concepts
Helm Repository Management
Helm charts require repositories to be available. TypeKro automatically infers repository names from URLs:
helmRelease({
name: 'redis',
chart: {
repository: 'https://charts.bitnami.com/bitnami', // Creates 'bitnami' HelmRepository
name: 'redis'
}
})
// For custom repositories, use descriptive names
helmRelease({
name: 'my-app',
chart: {
repository: 'https://charts.company.com/stable', // Creates 'stable' HelmRepository
name: 'my-app'
}
})
// For OCI repositories
helmRelease({
name: 'oci-app',
chart: {
repository: 'oci://registry.company.com/helm-charts', // Creates 'oci-app-helm-repo'
name: 'my-app'
}
})
Value Templating with TypeKro
Use TypeKro's magic proxy system for type-safe value templating:
const WebAppSchema = type({
name: 'string',
image: 'string',
hostname: 'string',
replicas: 'number',
resources: {
cpu: 'string',
memory: 'string'
}
});
const webappGraph = toResourceGraph(
{
name: 'webapp-helm',
apiVersion: 'example.com/v1alpha1',
kind: 'WebAppHelm',
spec: WebAppSchema
},
(schema) => ({
webapp: helmRelease({
name: schema.spec.name, // Dynamic resource name
chart: {
repository: 'https://charts.bitnami.com/bitnami',
name: 'nginx'
},
values: {
replicaCount: schema.spec.replicas,
image: {
repository: 'nginx',
tag: schema.spec.image
},
ingress: {
enabled: true,
hostname: schema.spec.hostname,
tls: true
},
resources: {
limits: {
cpu: schema.spec.resources.cpu,
memory: schema.spec.resources.memory
},
requests: {
cpu: schema.spec.resources.cpu,
memory: schema.spec.resources.memory
}
}
}
})
}),
(schema, resources) => ({
phase: resources.webapp.status.phase,
ready: Cel.expr<boolean>(resources.webapp.status.phase, ' == "Ready"'),
url: Cel.template('https://%s', schema.spec.hostname)
})
);
Cross-Resource References
Reference other Kubernetes resources in Helm values:
const fullStackGraph = toResourceGraph(
{
name: 'fullstack-app',
apiVersion: 'example.com/v1alpha1',
kind: 'FullStackApp',
spec: type({
name: 'string',
dbPassword: 'string',
appVersion: 'string'
})
},
(schema) => ({
// Create a secret first
dbSecret: secret({
name: 'db-credentials',
data: {
password: schema.spec.dbPassword,
username: 'webapp'
}
}),
// Deploy PostgreSQL via Helm
database: helmRelease({
name: 'postgres',
chart: {
repository: 'https://charts.bitnami.com/bitnami',
name: 'postgresql'
},
values: {
auth: {
existingSecret: 'db-credentials', // Reference the secret
database: 'webapp'
},
primary: {
persistence: {
enabled: true,
size: '10Gi'
}
}
}
}),
// Deploy application via Helm
webapp: helmRelease({
name: schema.spec.name,
chart: {
repository: 'https://charts.company.com/apps',
name: 'webapp'
},
values: {
image: {
tag: schema.spec.appVersion
},
database: {
host: 'postgres-postgresql', // Helm chart service name
port: 5432,
existingSecret: 'db-credentials'
}
}
})
}),
(schema, resources) => ({
databaseReady: Cel.expr<boolean>(resources.database.status.phase, ' == "Ready"'),
appReady: Cel.expr<boolean>(resources.webapp.status.phase, ' == "Ready"'),
ready: Cel.expr<boolean>(`
resources.database.status.phase == "Ready" &&
resources.webapp.status.phase == "Ready"
`)
})
);
Advanced Patterns
Multi-Chart Applications
For multi-chart microservices examples, see Microservices Architecture and Helm Patterns.
Key patterns for multi-chart applications:
- Shared Infrastructure: Deploy databases and caches first
- Service Dependencies: Frontend depends on backend, backend depends on database
- Environment Consistency: Same environment variables across all services
- Status Aggregation: Overall health depends on all services being ready
Environment-Specific Configurations
Configure different Helm values per environment. See Basic WebApp Pattern for environment-specific configuration patterns.
Environment configuration strategy:
// Environment-specific values
const configs = {
dev: { replicas: 1, resources: { cpu: '100m' }, ingress: false },
staging: { replicas: 2, resources: { cpu: '200m' }, ingress: true },
production: { replicas: 5, resources: { cpu: '500m' }, ingress: true }
};
const config = configs[schema.spec.environment];
// Apply config to Helm values...
Custom Helm Repositories
Configure custom Helm repositories for private charts:
const privateChartGraph = toResourceGraph(
{
name: 'private-app',
apiVersion: 'company.com/v1alpha1',
kind: 'PrivateApp',
spec: type({
name: 'string',
chartVersion: 'string',
credentials: {
username: 'string',
password: 'string'
}
})
},
(schema) => {
// Create secret for private repository first
const repoSecret = secret({
name: 'helm-repo-secret',
data: {
username: schema.spec.credentials.username,
password: schema.spec.credentials.password
}
});
// Create HelmRepository resource that references secret
const privateRepo = helmRepository({
name: 'private-charts',
namespace: 'flux-system',
url: 'https://charts.private.company.com',
secretRef: {
name: repoSecret.metadata.name // Reference secret by field
}
});
// Deploy using simple factory
const app = simpleHelmChart(
schema.spec.name,
privateRepo.spec.url, // Reference repository URL by field
'private-app',
{
// Application-specific values
}
);
return { repoSecret, privateRepo, app };
},
(schema, resources) => ({
repositoryReady: resources.privateRepo.status.ready,
appReady: Cel.expr<boolean>(resources.app.status.phase, ' == "Ready"'),
phase: Cel.expr<'Pending' | 'Installing' | 'Ready' | 'Failed'>(`
resources.privateRepo.status.ready && resources.app.status.phase == "Ready" ?
"Ready" : "Installing"
`)
})
);
Deployment Workflows
Development Workflow
For development with Helm charts:
// Quick development deployment
const devGraph = toResourceGraph(
{
name: 'dev-app',
apiVersion: 'example.com/v1alpha1',
kind: 'DevApp',
spec: type({ name: 'string', debug: 'boolean' })
},
(schema) => ({
app: simpleHelmChart(
schema.spec.name,
'https://charts.bitnami.com/bitnami',
'nginx',
{
service: { type: 'NodePort' },
ingress: { enabled: false },
resources: {
limits: { cpu: '100m', memory: '128Mi' }
},
// Enable debug mode
...(schema.spec.debug && {
extraEnvVars: [
{ name: 'DEBUG', value: 'true' },
{ name: 'LOG_LEVEL', value: 'debug' }
]
})
}
)
})
);
// Deploy to development
const factory = await devGraph.factory('direct', { namespace: 'dev' });
await factory.deploy({ name: 'my-dev-app', debug: true });
GitOps Workflow
For production deployments via GitOps:
// Generate YAML for different environments
const environments = ['staging', 'production'];
for (const env of environments) {
const yaml = envConfigGraph.toYaml();
writeFileSync(`k8s/${env}/app.yaml`, yaml);
}
// Commit and let ArgoCD/Flux deploy
// git add k8s/
// git commit -m "Deploy app v1.2.3 to staging and production"
// git push
CI/CD Integration
Integrate Helm deployments in CI/CD:
// ci-cd-deployment.ts
async function deployApp(version: string, environment: string) {
const factory = await appGraph.factory('kro', {
namespace: environment
});
try {
await factory.deploy({
name: 'my-app',
version: version,
environment: environment
});
// Wait for deployment to be ready
await factory.waitForReady(300000); // 5 minute timeout
console.log(`✅ Successfully deployed ${version} to ${environment}`);
// Run health checks
const status = await factory.getStatus();
if (!status.ready) {
throw new Error('Deployment not ready after timeout');
}
} catch (error) {
console.error(`❌ Deployment failed: ${error.message}`);
throw error;
}
}
// Usage in CI/CD
await deployApp(process.env.VERSION, process.env.ENVIRONMENT);
Troubleshooting
Common Issues
Helm Repository Not Found
// Issue: HelmRepository not available in cluster
// Solution: Ensure repository is created before HelmRelease
const graph = toResourceGraph(
{ /* spec */ },
(schema) => ({
// Create repository first
repo: helmRepository({
name: 'my-repo',
namespace: 'flux-system',
url: 'https://charts.example.com'
}),
// Then create release (will wait for repo)
app: helmRelease({
name: 'my-app',
chart: {
repository: 'https://charts.example.com',
name: 'my-chart'
}
})
})
);
Chart Version Conflicts
// Issue: Chart version not found or incompatible
// Solution: Specify exact versions and validate
helmRelease({
name: 'my-app',
chart: {
repository: 'https://charts.bitnami.com/bitnami',
name: 'nginx',
version: '13.2.23' // Always specify version for production
}
})
Value Serialization Issues
// Issue: Complex TypeKro references not serializing correctly
// Solution: Use CEL expressions for complex logic
const graph = toResourceGraph(
{ /* spec */ },
(schema) => ({
app: helmRelease({
name: 'my-app',
chart: { /* chart config */ },
values: {
// Don't do this - complex JavaScript logic
// replicas: schema.spec.environment === 'production' ? 5 : 1
// Do this - use simple references
replicas: schema.spec.replicas,
environment: schema.spec.environment
}
})
}),
// Handle complex logic in status
(schema, resources) => ({
replicas: Cel.expr<number>(`
schema.spec.environment == "production" ? 5 : 1
`)
})
);
Debug Commands
# Check HelmRepository status
kubectl get helmrepository -n flux-system
# Check HelmRelease status
kubectl get helmrelease -n your-namespace
# View Helm release details
kubectl describe helmrelease my-app -n your-namespace
# Check Flux logs
kubectl logs -n flux-system deployment/helm-controller
# View generated Helm values
kubectl get helmrelease my-app -o yaml | yq '.spec.values'
Status Monitoring
Monitor Helm deployments:
const factory = await helmGraph.factory('kro');
await factory.deploy(spec);
// Monitor deployment progress
factory.onStatusUpdate((status) => {
console.log(`Helm Release Phase: ${status.phase}`);
if (status.phase === 'Ready') {
console.log('✅ Deployment successful');
} else if (status.phase === 'Failed') {
console.error('❌ Deployment failed');
// Check Helm release events
factory.getEvents().then(events => {
console.error('Recent events:', events);
});
}
});
// Check specific conditions
factory.onFieldChange('phase', (phase) => {
if (phase === 'Installing') {
console.log('🔄 Installing Helm chart...');
}
});
Best Practices
Chart Selection
- Use official charts: Prefer Bitnami, official project charts
- Pin versions: Always specify chart versions for production
- Review values: Understand default values and override appropriately
- Test locally: Validate charts with
helm template
before deployment
Value Management
- Keep values simple: Use TypeKro references for simple value mapping
- Use CEL for logic: Complex logic belongs in CEL expressions, not Helm values
- Environment separation: Different value files/configurations per environment
- Secret management: Never put secrets in values, use Kubernetes secrets
Deployment Strategy
- Repository management: Centralize HelmRepository resources
- Namespace organization: Separate applications by namespace
- Dependency order: Deploy infrastructure charts before application charts
- Rollback planning: Understand Helm rollback capabilities
Security
- Private repositories: Use secrets for authentication
- RBAC: Ensure proper permissions for Helm operations
- Image scanning: Scan chart images for vulnerabilities
- Value validation: Validate values before deployment
Migration from Pure Helm
From Helm CLI
# Old: Direct Helm commands
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-nginx bitnami/nginx --set replicaCount=3
# New: TypeKro Helm integration
const graph = toResourceGraph(
{ /* schema */ },
(schema) => ({
nginx: helmRelease({
name: 'my-nginx',
chart: {
repository: 'https://charts.bitnami.com/bitnami',
name: 'nginx'
},
values: {
replicaCount: 3
}
})
})
);
From Helm Templates
Convert existing Helm templates to TypeKro:
# Old: values.yaml
replicaCount: 2
image:
repository: nginx
tag: 1.21.6
ingress:
enabled: true
hostname: app.example.com
// New: TypeKro schema-driven
const AppSchema = type({
replicas: 'number',
image: 'string',
hostname: 'string'
});
const graph = toResourceGraph(
{ spec: AppSchema, /* ... */ },
(schema) => ({
app: helmRelease({
name: 'nginx-app',
chart: { /* chart config */ },
values: {
replicaCount: schema.spec.replicas,
image: {
repository: 'nginx',
tag: schema.spec.image
},
ingress: {
enabled: true,
hostname: schema.spec.hostname
}
}
})
})
);
Next Steps
- KRO Integration - Advanced orchestration with KRO
- GitOps - Production GitOps workflows
- Examples - Real-world Helm integration examples
- Performance - Optimizing Helm deployments