Flux Factories
Factory functions for Flux CD Custom Resource Definitions.
Import
// Helm factories from main package
import { helmRelease, helmRepository } from 'typekro';
// Source factories from subpath
import { gitRepository } from 'typekro/flux';Quick Example
import { helmRelease, helmRepository } from 'typekro';
const repo = helmRepository({
name: 'bitnami',
url: 'https://charts.bitnami.com/bitnami',
id: 'bitnami'
});
const release = helmRelease({
name: 'redis',
chart: {
repository: 'https://charts.bitnami.com/bitnami',
name: 'redis'
},
values: { replica: { replicaCount: 3 } },
id: 'redis'
});Available Factories
| Factory | Import | Description |
|---|---|---|
helmRelease | typekro | Deploy Helm charts via Flux |
helmRepository | typekro | Helm chart repository source |
HelmChart | typekro/simple | Simplified Helm deployment |
gitRepository | typekro/flux | Git repository source |
helmRelease()
Full HelmRelease configuration for deploying Helm charts.
import { helmRelease } from 'typekro';
const release = helmRelease({
id: 'myApp',
name: 'my-app',
namespace: 'production',
interval: '10m',
chart: {
repository: 'https://charts.example.com',
name: 'my-chart',
version: '1.2.3'
},
values: {
replicaCount: 3,
image: { tag: 'v1.0.0' }
},
valuesFrom: [{
kind: 'Secret',
name: 'my-app-values',
valuesKey: 'databaseUrl',
targetPath: 'config.databaseUrl'
}],
postRenderers: [{
kustomize: {
patches: [{
target: { group: 'apps', version: 'v1', kind: 'Deployment', name: 'my-app' },
patch: '- op: remove\n path: /metadata/annotations/legacy'
}]
}
}]
});valuesFrom exposes Flux's Secret/ConfigMap value projection without copying the source value into TypeKro state. Its name, valuesKey, and targetPath fields are graph-aware, so resources created or observed in the same composition can provide their names through ordinary TypeKro references. Values projected through targetPath use Helm --set parsing semantics under the managed Flux 2.7.5 baseline. TypeKro intentionally withholds Flux 2.9's literal field until the managed runtime and Kubernetes compatibility baseline can move together. Use chart-native envFrom or mounted files for arbitrary Secret bytes; do not route credentials with punctuation through targetPath.
postRenderers exposes Flux's Kustomize post-rendering boundary for strategic-merge and JSON 6902 patches. Patch targets are graph-aware, so their names and namespaces may come from schema or resource references. Prefer chart-native values for ordinary configuration; use post-rendering when an upstream chart cannot express a required manifest transformation.
Install, upgrade and CRD policy
Every TypeKro HelmRelease factory accepts the same three Flux lifecycle options. That covers the generic helmRelease and every integration factory (certManagerHelmRelease, traefikHelmRelease, cnpgHelmRelease, ...):
| Option | Fields |
|---|---|
install | timeout, crds (Skip / Create / CreateReplace), createNamespace, remediation.retries, remediation.remediateLastFailure, remediation.ignoreTestFailures |
upgrade | timeout, crds, remediation.retries, remediation.remediateLastFailure, remediation.ignoreTestFailures, remediation.strategy |
driftDetection | mode (enabled / warn / disabled), ignore |
Each field you set overrides the factory's default for that field, and remediation is merged field by field too. driftDetection replaces the default as a whole.
import { traefikHelmRelease } from 'typekro/traefik';
const release = traefikHelmRelease({
name: 'traefik',
install: { timeout: '20m' },
upgrade: { crds: 'CreateReplace', remediation: { retries: 5 } },
});With remediation.retries above 0, Flux remediates every failure, the last one included: a failed install is uninstalled and tried again, and a failed upgrade is rolled back (or uninstalled, with strategy: 'uninstall') and tried again. Set retries: 0 for a chart that must never be rolled back automatically.
In KRO mode the options can be schema references, either field by field (install: { timeout: spec.installTimeout }) or as whole objects (install: spec.install). Each field that has a default renders as Cel.default(<instance field>, <default>), so an instance that leaves it unset still gets the factory's default, the same as in direct mode. Fields your schema does not declare keep their defaults and are never read from the instance. A schema field passed as retries must be declared 'number.integer': the HelmRelease takes an integer, and KRO types a plain 'number' as a float. The factory throws a ValidationError at build time for a 'number' field, and in direct mode for a fractional value such as 2.7, so both modes reject what the CRD would. TypeKro never rounds or truncates the value. A reference to another resource (for example a ConfigMap field) gets no fallback: KRO waits for that field as usual.
Unless a factory sets its own policy, it uses the defaults of the generic helmRelease: install.timeout and upgrade.timeout of 10m, and 3 remediation retries for each. Without them Flux uses a 5m timeout and no retries, so a slow first install stays Stalled until someone runs flux reconcile helmrelease <name> --reset. These factories set their own policy:
| Factory | Default |
|---|---|
hatchetHelmRelease, harborHelmRelease | 20m timeouts. Failed upgrades are not retried or rolled back, because they may run database migrations |
envoyGatewayHelmRelease and the Envoy AI Gateway releases | 15m timeouts, remediateLastFailure, rollback when an upgrade fails |
traefikHelmRelease | crds: CreateReplace on install and upgrade (from its crds option), rollback when an upgrade fails, timeouts from spec.timeout |
apisixHelmRelease | install.createNamespace: true, timeouts from spec.timeout |
crowdsecHelmRelease | Rollback when an upgrade fails, timeouts from spec.timeout (15m, for the registration Jobs and first-start hub downloads), no drift detection |
kedaHelmRelease | Rollback when an upgrade fails, drift detection enabled, timeouts from spec.timeout (10m). Its CRDs are chart templates (crds.install), so no Flux CRD policy is set |
vpaHelmRelease | crds: CreateReplace on install and upgrade, rollback when an upgrade fails, drift detection enabled, timeouts from spec.timeout (10m) |
karpenterHelmRelease, karpenterCrdHelmRelease | crds: Skip on install and upgrade for the controller chart, rollback when an upgrade fails, drift detection enabled, timeouts from spec.timeout (10m) |
ciliumHelmRelease | Timeouts from its installTimeout and upgradeTimeout options, or from timeout for both. 10m when none is set |
Flux's own CRD defaults are Create on install and Skip on upgrade. A chart that ships CRDs in its crds/ directory therefore keeps the CRDs of the version it was first installed at, until you set upgrade.crds: CreateReplace. The CRD policy applies only to files in a chart's crds/ directory. Charts that render their CRDs as templates, such as cert-manager (crds.enabled), ignore it.
A custom integration factory can reuse the same merge through helmReleaseLifecycle from typekro/helm:
import { helmReleaseLifecycle } from 'typekro/helm';
declare const config: { install?: { timeout?: string } };
// Spread into the HelmRelease spec the factory builds itself.
const lifecycle = helmReleaseLifecycle(config, { driftDetection: { mode: 'enabled' } });helmRepository()
Creates a Flux HelmRepository resource.
import { helmRepository } from 'typekro';
const repo = helmRepository({
id: 'bitnamiRepo',
name: 'bitnami',
namespace: 'flux-system',
url: 'https://charts.bitnami.com/bitnami',
interval: '10m'
});Type-Safe Helm Values
values is a recursive TypeKro value tree. In graph mode, TypeKro walks the object and serializes supported data leaves automatically:
- primitives, arrays, and plain objects
- schema and resource magic-proxy references
- CEL expressions
- mixed template strings such as
`https://${spec.host}`
Unsupported runtime-only leaves are rejected with a path-specific error. Do not put functions, callbacks, clients, symbols, class instances, or live cluster reads inside values; move that logic to a first-class factory field or a direct-only API instead. undefined fields are omitted recursively.
Schema references work naturally in Helm values:
import { kubernetesComposition, helmRelease } from 'typekro';
const app = kubernetesComposition(definition, (spec) => {
const release = helmRelease({
id: 'app',
name: 'app',
chart: { repository: 'https://charts.example.com', name: 'myapp' },
values: {
replicaCount: spec.replicas, // Schema reference
image: { tag: spec.version }, // Nested reference
config: {
database: spec.dbName,
publicUrl: `https://${spec.host}`,
logLevel: spec.logLevel
}
}
});
return {
ready: release.status.conditions?.[0]?.status === 'True'
};
});Usage in Compositions
import { type } from 'arktype';
import { kubernetesComposition, helmRelease } from 'typekro';
const DatabaseSpec = type({
name: 'string',
size: 'string',
replicas: 'number'
});
const database = kubernetesComposition({
name: 'database',
apiVersion: 'data.example.com/v1',
kind: 'Database',
spec: DatabaseSpec,
status: type({ ready: 'boolean' })
}, (spec) => {
const db = helmRelease({
id: 'db',
name: spec.name,
chart: {
repository: 'https://charts.bitnami.com/bitnami',
name: 'postgresql',
version: '12.x'
},
values: {
primary: { persistence: { size: spec.size } },
readReplicas: { replicaCount: spec.replicas }
}
});
return {
ready: db.status.conditions?.[0]?.status === 'True'
};
});Prerequisites
HelmRelease requires Flux CD installed in your cluster:
# Install Flux CLI
brew install fluxcd/tap/flux
# Bootstrap Flux
flux bootstrap github \
--owner=<your-org> \
--repository=<your-repo> \
--path=clusters/my-clusterOr use the TypeKro Runtime Bootstrap to install Flux and Kro together.
Next Steps
- YAML Closures - Alternative deployment methods
- Kubernetes Factories - Core Kubernetes resources
- Runtime Bootstrap - Install Flux and Kro