Skip to content
Docs menu / konfig build

konfig build

konfig build <env> turns one environment into a tree of YAML files on disk. It loads the env’s entry module, runs the app-of-apps (or bundle) program that module exports, and renders every manifest with the render context built from the shared flags. The result is one YAML file per Kubernetes resource plus one Argo CD Application per app. If nothing that feeds the render has changed since the last run, the build writes nothing and reports a cache hit.

Usage

Terminal window
konfig build <env> [--log text|json] [--verbose] [--no-cache]
[--cluster <name>] [--k8s-version <ver>] [--flag k=v ...]

Arguments

NameDefaultDescription
envrequiredEnv name to build (for example prod). Resolved via envs.<env>.entry or <root>/env/<env>.ts.

Flags

NameDefaultDescription
--logtextOutput format for the report line: text or json.
--verbosefalseWrap the render in an Effect span named konfig.render.<env> so tracing output shows per-render timing.
--no-cachefalseSkip the input-hash check and force a fresh render (debugging, first build).
--clusterunsetShared render flag, see CLI overview. Adds a <cluster> directory level to the output path.
--k8s-versionunsetShared render flag.
--flag k=vunsetShared render flag, repeatable.

Output layout

Files land under <configDir>/<root>/<outDir.manifests>/<env>, with an extra /<cluster> level when --cluster is set. That directory contains two kinds of subdirectories:

  • One directory per app, named after the app. It holds one file per rendered resource, named <Kind>-<name>.yaml. Dots and slashes in resource names are replaced with - in the filename. Output that arrives as raw YAML text (Helm releases, sops passthrough) is split on real YAML document boundaries, so it ends up in the same per-resource files as everything else.
  • One directory for the app-of-apps program itself, named apps unless you gave the program a name. It holds one Application-<app>.yaml for every child app.
.generated/manifests/prod/
├── apps/
│ ├── Application-api.yaml
│ └── Application-postgres.yaml
├── api/
│ ├── Deployment-api.yaml
│ └── Service-api.yaml
└── postgres/
└── ...

Writes are atomic. Everything is first staged under <outDir>.tmp; then the live directory is removed and the staging directory is renamed into place, so a failed run never leaves a half-written tree. Files are written with mode 0600. Apps render four at a time, which keeps the number of helm and sops subprocesses bounded.

Cache

The cache lets repeated builds finish without re-rendering. Before rendering, build computes a SHA-256 hash over everything that can influence the output:

  • the decoded konfig.json
  • the render context (--cluster, --k8s-version, and the sorted --flag pairs)
  • the env entry file
  • every file under root, skipping node_modules, dist, and .konfig
  • every path matched by cacheInclude

The input hash and a hash of the written output are stored in <configDir>/.konfig/cache/<env>-<ctxKey>.json. The context key is a digest of the render context, so --cluster and --flag combinations each get their own slot and never overwrite each other.

A run counts as a cache hit only when all of the following hold: the input hash matches, the recorded output directory is the one this invocation would write to, that directory exists, and its on-disk contents still hash to the recorded output hash. Editing or deleting a generated file by hand therefore causes a miss, and the next build rewrites the tree. The hash is deliberately conservative: touching any file under root invalidates it.

Report

In text mode the command prints Rendering env 'prod'... followed by one report line. After a real render that line reads Wrote 42 file(s) to /abs/out/prod and includes the render and write times in milliseconds. On a cache hit it reads Cached, notes that the env’s inputs are unchanged, and states the file count at the output directory.

With --log json the command prints a single JSON object per run:

{"env":"prod","files":42,"outDir":"/abs/out/prod","renderMs":812,"writeMs":9,"totalMs":821,"cached":false}

Exit codes and errors

The process exits non-zero when the program fails. The tagged errors you will most commonly see:

ErrorWhen
ConfigNotFoundNo konfig.json was found walking up from the current directory.
ConfigParseErrorkonfig.json exists but does not match the schema.
EnvEntryNotFoundThe env’s entry file does not exist.
EnvLoadErrorThe entry has no default export, or the default export is not an Effect program.
render errors from your modulesFor example HelmVersionTooLow, HelmDigestMismatch, or an unbound secret.
WriteEnvErrorWriting the output tree failed.
BuildCacheErrorReading or writing the cache file failed.

Example

Terminal window
konfig build prod # first run renders and writes
konfig build prod # second run: Cached, inputs unchanged
konfig build prod --cluster eu-west-1 # separate output dir and cache slot
konfig build prod --no-cache --log json # force render, machine-readable report