Migration Guide
Migrate to TypeKro from existing infrastructure tools incrementally.
From Raw YAML
Use YamlFile() to include existing manifests while adding new TypeKro resources:
import { kubernetesComposition } from 'typekro';
import { Service, YamlFile } from 'typekro/simple';
import { type } from 'arktype';
const app = kubernetesComposition({
name: 'migrated-app',
apiVersion: 'example.com/v1',
kind: 'MigratedApp',
spec: type({ name: 'string' }),
status: type({ ready: 'boolean' })
}, (spec) => {
// Include existing YAML - no changes needed
YamlFile('./k8s/existing-deployment.yaml');
// Add new TypeKro resources alongside
const service = Service({
id: 'svc',
name: spec.name,
selector: { app: spec.name },
ports: [{ port: 80 }]
});
return { ready: true };
});Migrate incrementally:
- Start by wrapping existing YAML with
YamlFile() - Add new resources using TypeKro factories
- Gradually replace YAML files with TypeKro equivalents
From Helm
Option 1: Replace Helm Templates
Convert Helm templates to type-safe TypeKro:
// Before: values.yaml + templates/deployment.yaml
// After: Pure TypeScript
import { kubernetesComposition } from 'typekro';
import { Deployment } from 'typekro/simple';
import { type } from 'arktype';
const app = kubernetesComposition({
name: 'webapp',
apiVersion: 'example.com/v1',
kind: 'WebApp',
spec: type({
name: 'string',
replicas: 'number',
image: 'string'
}),
status: type({ ready: 'boolean' })
}, (spec) => {
const deploy = Deployment({
id: 'app',
name: spec.name,
image: spec.image,
replicas: spec.replicas
});
return { ready: deploy.status.readyReplicas > 0 };
});Option 2: Keep Existing Charts
Use helmRelease() to deploy existing Helm charts with type-safe values:
import { kubernetesComposition, helmRelease, helmRepository } from 'typekro';
import { type } from 'arktype';
const app = kubernetesComposition({
name: 'nginx-app',
apiVersion: 'example.com/v1',
kind: 'NginxApp',
spec: type({ replicas: 'number' }),
status: type({ ready: 'boolean' })
}, (spec) => {
const repo = helmRepository({
id: 'bitnami',
name: 'bitnami',
url: 'https://charts.bitnami.com/bitnami'
});
const release = helmRelease({
id: 'nginx',
name: 'nginx',
chart: {
repository: 'https://charts.bitnami.com/bitnami',
name: 'nginx'
},
values: {
replicaCount: spec.replicas // Type-safe values from spec
}
});
return { ready: true };
});From CDK8s
Replace CDK8s constructs with TypeKro factories:
// Before (CDK8s):
// new KubeDeployment(this, 'deployment', {
// spec: { replicas: 3, ... }
// });
// After (TypeKro):
import { Deployment } from 'typekro/simple';
const deploy = Deployment({
id: 'deployment',
name: 'my-app',
image: 'nginx',
replicas: 3
});Key differences from CDK8s:
- No construct tree - resources auto-register in composition context
- Runtime references via CEL expressions (not just deploy-time)
- Direct deployment without synth step
- Status expressions for runtime state
From Pulumi
Replace Pulumi resources with TypeKro:
// Before (Pulumi):
// const deployment = new k8s.apps.v1.Deployment(...);
// export const ip = deployment.status.loadBalancer.ingress[0].ip;
// After (TypeKro):
import { kubernetesComposition } from 'typekro';
import { Service } from 'typekro/simple';
const app = kubernetesComposition({
name: 'webapp',
apiVersion: 'example.com/v1',
kind: 'WebApp',
spec: type({ name: 'string' }),
status: type({ ip: 'string' })
}, (spec) => {
const svc = Service({
id: 'svc',
name: spec.name,
type: 'LoadBalancer',
ports: [{ port: 80 }]
});
return {
ip: svc.status.loadBalancer.ingress[0].ip // ingress[0] is valid in CEL — Enhanced types are NonOptional in status builder context
};
});Key differences from Pulumi:
- Stateless - no state backend required
- GitOps-ready YAML output via
toYaml() - Runtime references via CEL (evaluated by Kro, not at deploy-time)
- No provider configuration needed
Between TypeKro APIs
TypeKro has two composition APIs: kubernetesComposition (recommended) and toResourceGraph (advanced). Here's how to migrate between them.
From toResourceGraph to kubernetesComposition
Most compositions should use kubernetesComposition. To migrate:
// Before: toResourceGraph (separate builders, explicit CEL)
import { toResourceGraph, Cel, createDeployment, createService } from 'typekro';
const app = toResourceGraph(
{ name: 'app', apiVersion: 'example.com/v1', kind: 'App',
spec: type({ name: 'string', replicas: 'number' }),
status: type({ ready: 'boolean', url: 'string' }) },
(schema) => ({
deploy: createDeployment({ name: schema.spec.name, replicas: schema.spec.replicas }),
svc: createService({ name: schema.spec.name, ports: [{ port: 80 }] }),
}),
(_schema, resources) => ({
ready: Cel.expr<boolean>(resources.deploy.status.readyReplicas, ' > 0'),
url: Cel.template('http://%s', resources.svc.status.clusterIP),
})
);
// After: kubernetesComposition (single function, natural JS)
import { kubernetesComposition } from 'typekro';
import { Deployment, Service } from 'typekro/simple';
const app = kubernetesComposition(
{ name: 'app', apiVersion: 'example.com/v1', kind: 'App',
spec: type({ name: 'string', replicas: 'number' }),
status: type({ ready: 'boolean', url: 'string' }) },
(spec) => {
const deploy = Deployment({ id: 'deploy', name: spec.name, replicas: spec.replicas });
const svc = Service({ id: 'svc', name: spec.name, ports: [{ port: 80 }] });
return {
ready: deploy.status.readyReplicas > 0,
url: `http://${svc.status.clusterIP}`
};
}
);Key changes:
- Replace
createDeployment()/createService()withsimple.Deployment()/simple.Service()(or import fromtypekro/simple) - Merge the two builder functions into one — create resources, then return status
- Replace
Cel.expr()with natural JavaScript:Cel.expr<boolean>(ref, ' > 0')becomesref > 0 - Replace
Cel.template()with template literals:Cel.template('http://%s', ref)becomes`http://${ref}` - Replace
schema.spec.namewithspec.name(parameter is the spec directly, not a schema wrapper) - Add
idto every resource for cross-resource references
From kubernetesComposition to toResourceGraph
Switch to toResourceGraph when you need explicit CEL control:
- Split the composition function into a resource builder (returns a keyed object) and a status builder
- Replace
simple.Deployment()withcreateDeployment()etc. - Remove
idfrom resources — the key in the returned object serves the same purpose - Convert JavaScript status expressions to
Cel.expr()/Cel.template() - Replace
spec.namewithschema.spec.name
Exporting Static YAML
Export compositions as static YAML for review or GitOps:
import { writeFileSync } from 'fs';
// Generate YAML for review
const yaml = webapp.toYaml();
console.log(yaml);
// Write to file for GitOps
writeFileSync('./manifests/webapp.yaml', yaml);The generated YAML works with any Kubernetes tooling:
kubectl apply -f manifests/- ArgoCD Application pointing to the manifests directory
- Flux Kustomization
Compatibility Matrix
| Tool | Compatibility | Notes |
|---|---|---|
| kubectl | ✅ Full | Apply generated YAML directly |
| ArgoCD | ✅ Full | GitOps workflows with generated manifests |
| Flux | ✅ Full | HelmRelease integration, Kustomization support |
| Kustomize | ✅ Full | Use YamlFile() with kustomization.yaml |
| Helm | ✅ Full | helmRelease() for existing charts |
Kro Controller Requirements
Some TypeKro features require the Kro controller:
| Feature | Without Kro | With Kro |
|---|---|---|
| Resource deployment | ✅ Direct mode | ✅ Kro mode |
| Cross-resource references | ❌ Static only | ✅ Runtime CEL |
| Status expressions | ❌ Not evaluated | ✅ Runtime evaluation |
| Status propagation | ❌ Manual | ✅ Automatic |
For static deployments without runtime features, use Direct mode:
const factory = webapp.factory('direct', { namespace: 'default' });
await factory.deploy({ name: 'app', image: 'nginx' });For full runtime features, deploy the Kro controller first:
import { typeKroRuntimeBootstrap } from 'typekro';
// Bootstrap Kro controller
const runtime = typeKroRuntimeBootstrap();
const runtimeFactory = runtime.factory('direct', { namespace: 'kro-system' });
await runtimeFactory.deploy({});
// Now use Kro mode for runtime features
const factory = webapp.factory('kro', { namespace: 'default' });
await factory.deploy({ name: 'app', image: 'nginx' });Gradual Adoption Strategy
- Week 1: Wrap existing YAML with
yamlFile() - Week 2: Add new resources using TypeKro factories
- Week 3: Replace simple YAML files with TypeKro equivalents
- Week 4: Add status expressions for runtime state
- Ongoing: Migrate remaining resources as needed
Next Steps
- Getting Started - Quick start guide
- Deployment Modes - Direct vs Kro deployment
- YAML Integration - YamlFile and HelmChart
- Helm Integration - HelmRelease examples