Introduction
konfig.ts lets you describe a Kubernetes environment in TypeScript and render it to plain YAML that Argo CD can sync. It ships as a set of @konfig.ts/* packages built on Effect, plus a konfig CLI that loads your .ts sources, renders them, and writes the manifests and Argo CD Application resources into an output directory.
The point is not YAML generation by itself. konfig.ts wires two layers of an application from one TypeScript source:
- The manifest layer: Deployments, Services, Secrets, Helm releases, Argo CD Applications, and the dependency graph between them.
- The runtime config layer: the environment variables your process reads at startup, decoded with Effect
Config.
Because both layers come from the same declarations, mistakes that would otherwise show up during argocd sync become TypeScript errors or fail-closed render errors instead. Examples: a workload referencing a Secret nobody created, an env var read in code but missing from the pod spec, or a chart pinned by version but not by digest.
What you get
The dependency graph is checked at the type level. Each module declares what it needs (a Secret, a Namespace, a database) and what it provides. When you compose modules into an environment, fromModules refuses to compile until every need has a provider. See Dependency graph.
Environment variables have one contract for both sides. You declare each variable once, as a secret, a literal, or a downward-API value. Binding that contract produces the Deployment env block and the Secret resources; the same contract decodes the variables inside the running process. Rename a variable once and both sides move.
Rendered YAML is deterministic. Output uses stable key ordering (apiVersion, kind, metadata, spec, status first, then alphabetical) and is emitted as YAML 1.1, so kubectl and go-yaml readers do not coerce strings like yes or on. konfig diff compares rendered output against a baseline structurally, so reordered keys and multi-document files do not show up as noise.
Secret backends are typed values. sops, sealed-secrets, and external-secrets each turn a declared secret into the matching custom resource, with schema validation on the binary’s output. Whether a backend requires a source file is encoded in its type, so forgetting the source for a sops backend is a compile error.
Helm releases are digest-verified. Every release must name a chart digest. The cached chart tarball is hashed on every pull and every cache hit, so a chart whose bytes change fails the next render with HelmDigestMismatch.
A compile error instead of a sync failure
The example below is one of the compile-time-catch files shipped in examples/full-stack/infra/envs/. The api module asks for a Secret named ghcr-pull, but no module in the list provides it. fromModules rejects the composition at compile time, before anything is rendered.
const api = defineApi({
name: "api",
source: src("api"),
replicas: 1,
sopsBase: "infra/secrets"
})
const worker = defineWorker({
name: "worker",
source: src("worker"),
replicas: 1,
sopsBase: "infra/secrets"
})
export default AppOfApps.fromModules({error TS2345: Missing provider for Image "api", Image "worker" and Secret "ghcr-pull".
Add a module that provides them to AppOfApps.fromModules({ modules }), or check that providers come before consumers in the list.
target: { repoURL: cluster.repositoryUrl, branch, rootPath },
defaults: {},
modules: [api, worker]
})What konfig.ts is not
It does not replace kustomize. If you have hand-written YAML and want to overlay it, konfig.ts is the wrong tool. It owns the manifest source.
It does not mutate anything at runtime. It emits manifests; Argo CD or kubectl applies them. There is no admission controller and no operator.
It is not a platform abstraction. No Crossplane, no OAM, no “Service” model that hides Deployment, Service, and Ingress. Workload.web is a helper that emits those resources explicitly.
It does not replace Helm. It shells out to helm template. Charts you depend on stay charts; each templated document is lifted into the render as a raw YAML manifest.
Requirements
The konfig CLI imports your .ts sources at runtime, so it needs a TypeScript-capable runtime:
| Runtime | Status |
|---|---|
| Bun | Recommended, runs .ts with no flags |
| Node >= 22.18 or >= 23.6 | Works, native type stripping (packages declare engines.node >= 22) |
| Node 22.6 to 22.17 | Works with --experimental-strip-types |
tsx | Works: tsx node_modules/.bin/konfig ... |
Rendering Helm-based modules calls the helm binary; konfig validate --strict calls kubeconform. Type-checking and in-memory renders of manifest-only projects need neither.
Next
Continue with Installation, then build one Application end to end in Your first environment. To understand what the compile error above catches, read Dependency graph.