Use when the user wants to manage application configuration files (properties, env, toml, ini, yaml, json, text) and deploy them as Kubernetes ConfigMaps — phrases like "I have a .env / .properties / application.yaml file, how do I use it with ConfigHub?", "generate a ConfigMap like kustomize configMapGenerator", "like kubectl create configmap but versioned", "inject env vars via envFrom", "ConfigMap with content hash for rolling restart", "mutable vs immutable ConfigMap", "validate my application config with a schema", "propagate config changes to the workload without kubectl edit". Authors an AppConfig Unit in the right toolchain, sets up the ConfigMapRenderer Target, links the rendered ConfigMap to the workload via Needs/Provides, and picks immutable (hashed name, history) or mutable (stable name + hash annotation) mode. Do not load for authoring a Kubernetes `ConfigMap` resource directly in a `Kubernetes/YAML` Unit (use `config-as-data` + `cub-mutate`), for Secrets (separate SecretStore story — see `references/yaml-patterns.md`), or for migrating Helm values files (use `import-from-helm` — charts already render their own ConfigMaps).
Turn a user's application configuration file — .env, .properties, .yaml, .json, .toml, .ini, or plain text — into a versioned ConfigHub Unit, then render and deploy it as a Kubernetes ConfigMap via the built-in ConfigMapRenderer bridge.
Canonical doc: https://docs.confighub.com/markdown/guide/app-config.md.
ConfigHub's AppConfig/* toolchains let the user keep config in its native format (devs read .properties like .properties, not as wrapped YAML) while everything else in ConfigHub still works: revision history, set-string-path / set-int-path / set-bool-path mutations, vet-jsonschema validation, variant / upstream-downstream, Needs/Provides. The renderer then ships it as a ConfigMap — either an immutable hashed one (Kustomize-style, supports rolling updates with old pods still reading old ConfigMaps) or a mutable stable-named one (with a content-hash annotation on the pod template to trigger rolling restarts).
| Toolchain | File | When |
|---|---|---|
AppConfig/Env | .env | envFrom injection (pair with --option AsKeyValue=true); simple key=value. |
AppConfig/Properties | .properties | Java apps. |
AppConfig/YAML | .yaml | Most app frameworks; full structured config. |
AppConfig/JSON | .json | Node / JVM apps that prefer JSON. |
AppConfig/TOML | .toml | Rust / Python apps. |
AppConfig/INI | .ini | Legacy apps. |
AppConfig/Text | .txt | Plain text; metadata in YAML frontmatter delimited by ---. |
Pick the format before creating the Unit — ToolchainType is set at Unit-create and not changeable afterward. Matching what the application already reads is almost always right; a one-way conversion to a "better" format is extra churn without payoff.
Every AppConfig file must carry two ConfigHub metadata fields — ConfigHub strips them when rendering the ConfigMap:
configHub.configName — a unique name for this config file. Also becomes the ConfigMap data key (with the format's file suffix appended: MyApplicationConfig.ini).configHub.configSchema — a schema identifier, conceptually like a Kubernetes resource type (apps/v1/Deployment). Used with vet-jsonschema for validation, and as the first positional argument to set-*-path functions when mutating values.Format-specific syntax:
configHub: key (YAML) or "configHub": { ... } (JSON).[configHub] section.configHub.configName=... / configHub.configSchema=... lines.---, fields under a configHub key.See the published doc for side-by-side examples in each format.
kubectl rollout restart.kubectl create configmap" / "like configMapGenerator" / "versioned ConfigMap.".env as container environment variables via envFrom.vet-jsonschema).ConfigMap YAML directly (use config-as-data + cub-mutate). That path is fine for small static ConfigMaps with no rendering / history / hashing story.references/yaml-patterns.md.ConfigMaps — the chart already renders them; use import-from-helm.import-from-flux / import-from-argocd handle those.cub organization list succeeds and shows the right organization (proves a valid token; cub context get / cub info / cub version don't require one).RevisionHistoryLimit=0, stable name, confighub.com/Hash annotation on pod template). If the user doesn't know, recommend immutable for workload-config-with-rolling-updates; mutable for simpler single-cluster cases where the content rarely changes or a single stable name matters for observability tooling.envFrom vs volume decided — .env units with AsKeyValue=true can be consumed via envFrom; other formats mount as a volume file.Include the two metadata fields per the format. Example app.env:
configHub.configName=MyApplicationConfig
configHub.configSchema=SimpleApp
APP_FEATURES_0=authentication
APP_FEATURES_1=logging
APP_NAME=MyApplication
APP_VERSION=1.0.0
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_SSL_ENABLED=trueNote for AppConfig/Env: all values are treated as strings. The other AppConfig ToolchainTypes, other than AppConfig/Text, support int and bool values as well.
cub unit create --space <space> <config-slug> <file> \
--toolchain AppConfig/<Fmt> \
--change-desc "Seed <config-slug> application config from <file>.
User prompt: <verbatim>
Clarifications: <condensed — e.g. 'source: ./app.env at <git ref>'>"ToolchainType is locked in here. If the file needs edits afterward, prefer cub function set over re-creating the Unit (you'd lose the revision history).
set-*-path functions take the configSchema as the first positional argument, then the dotted path, then the value:
cub function set --space <space> --unit <config-slug> --toolchain AppConfig/Env \
--change-desc "Point DATABASE_HOST at prod. User prompt: <verbatim>. Clarifications: <condensed>" \
-o mutations \
-- set-string-path SimpleApp DATABASE_HOST postgres.prod.internal
cub function set --space <space> --unit <config-slug> --toolchain AppConfig/Properties \
--change-desc "Turn off dev-only flag. User prompt: <verbatim>. Clarifications: <condensed>" \
-- set-bool-path SimpleApp database.ssl.enabled false
cub function set --space <space> --unit <config-slug> --toolchain AppConfig/Properties \
--change-desc "Bump DB port. User prompt: <verbatim>. Clarifications: <condensed>" \
-- set-int-path SimpleApp database.port 5433For schema validation (requires a schema registered with ConfigHub):
cub function vet --space <space> --unit <config-slug> --toolchain AppConfig/INI -- vet-jsonschemavet-jsonschema works for all AppConfig ToolchainTypes, not just AppConfig/JSON and AppConfig/YAML.
The ConfigMapRenderer runs inside the ConfigHub server — no external Worker process required. Create (or re-create idempotently) the server-worker:
cub worker create --space default --allow-exists --is-server-worker server-workerThe Space is flexible; default/server-worker is the conventional home. Reference it cross-Space via <space>/<worker> syntax.
Pick mode via RevisionHistoryLimit:
# Immutable mode (default, 10 revisions retained for rolling updates).
cub target create --space <space> <target-slug> '' default/server-worker \
--provider ConfigMapRenderer \
--toolchain AppConfig/<Fmt> \
--livestate-type Kubernetes/YAML
# Immutable, custom retention.
cub target create --space <space> <target-slug> '' default/server-worker \
--provider ConfigMapRenderer \
--toolchain AppConfig/<Fmt> \
--livestate-type Kubernetes/YAML \
--option RevisionHistoryLimit=5
# Mutable (single stable-named ConfigMap, hash annotation for rolling restarts).
cub target create --space <space> <target-slug> '' default/server-worker \
--provider ConfigMapRenderer \
--toolchain AppConfig/<Fmt> \
--livestate-type Kubernetes/YAML \
--option RevisionHistoryLimit=0For .env + envFrom, also set AsKeyValue=true so each key-value pair becomes a ConfigMap data entry (not one big file):
cub target create --space <space> <target-slug>-kv '' default/server-worker \
--provider ConfigMapRenderer \
--toolchain AppConfig/Env \
--livestate-type Kubernetes/YAML \
--option AsKeyValue=truecub unit set-target --space <space> <config-slug> <target-slug>
cub unit apply --space <space> <config-slug> --waitInspect the rendered ConfigMap:
cub unit livestate --space <space> <config-slug> # full live resource (see references/cub-cli.md)Kubernetes/YAML sink Unit for the rendered ConfigMap(s)The renderer produces one or more ConfigMaps (one per revision in immutable mode, or one stable ConfigMap in mutable mode). A dedicated sink Unit holds them so downstream workloads can link to a single reference:
cub unit create --space <space> <configmap-slug>
cub link create --space <space> - <configmap-slug> <namespace-slug> # resolve namespace placeholder
cub link create --space <space> --wait - <configmap-slug> <config-slug> \
--use-live-state --auto-update --update-type MergeUnitsThe <configmap-slug> ← <config-slug> link with --use-live-state --auto-update --update-type MergeUnits is what keeps the sink Unit populated with the latest rendered ConfigMap(s) as they change.
Now wire the workload into the sink so Needs/Provides resolves the ConfigMap reference:
cub link create --space <space> - <workload-slug> <configmap-slug>Immutable mode — ConfigMap name changes per revision; use confighubplaceholder in the workload's ConfigMap references:
spec:
template:
spec:
containers:
- name: main
volumeMounts:
- name: config-volume
mountPath: /etc/app/app.properties
subPath: app.properties
volumes:
- name: config-volume
configMap:
name: confighubplaceholder # resolved to the latest hashed nameOptionally scope the link to the latest rendered revision so older ConfigMaps aren't in scope:
cub link create --space <space> - <workload-slug> <configmap-slug> \
--where-resource "metadata.annotations.confighub~1com/RenderRevision = 'Latest'"(~1 is the JSON-Pointer-like escape for . in the annotation key.)
Mutable mode — stable name, triggered by a hash annotation on the pod template:
spec:
template:
metadata:
annotations:
confighub.com/Hash: confighubplaceholder # resolved to the content hash
spec:
containers:
- name: main
volumeMounts:
- name: config-volume
mountPath: /etc/app/app.properties
subPath: app.properties
volumes:
- name: config-volume
configMap:
name: my-config # the Unit slug (no hash suffix)When ConfigHub renders a new ConfigMap, the Hash annotation on the pod template changes, which Kubernetes treats as a pod-template change and triggers a rolling update.
envFrom injection (either mode — use confighubplaceholder in immutable mode or the stable name in mutable mode):
spec:
template:
spec:
containers:
- name: main
envFrom:
- configMapRef:
name: confighubplaceholdercub unit create/update/set-target/apply, cub function set / cub function vet (AppConfig-aware set-*-path / vet-jsonschema), cub worker create --is-server-worker, cub target create/update for ConfigMapRenderer, cub link create/update for Needs/Provides wiring, read-only cub unit livestate/livedata/diff/get/list, read-only kubectl get/describe on the resulting ConfigMap for verification.kubectl create/edit configmap (bypasses ConfigHub), writing raw ConfigMap YAML into an AppConfig/* Unit (wrong toolchain), mixing multiple configuration schemas into one Unit (configSchema is one per Unit), rendering Secrets through this path (use a SecretStore).configHub.configName or configHub.configSchema — stop; fix the file first (functions like set-*-path depend on configSchema as argument).config-as-data or import-from-cluster to bring the Namespace into ConfigHub first.RevisionHistoryLimit caps it. For longer retention, raise the limit explicitly; do not try to preserve old ConfigMaps out of band.cub unit livestate --space <space> <config-slug> — shows the rendered ConfigMap resource.cub unit list --space <space> -o jq='.[] | select(.Unit.Slug == "<configmap-slug>") | .Unit | {HeadRevisionNum, LiveRevisionNum, LastAppliedRevisionNum}' — sink Unit caught up.kubectl get configmap -n <ns> — immutable mode: hashed name ending in -<hash>; mutable mode: stable name = Unit slug.kubectl get pod -n <ns> -l <selector> -o jsonpath='{.items[0].spec.template.metadata.annotations.confighub\.com/Hash}{"\n"}' — matches the ConfigMap's confighub.com/Hash annotation; pod restarts on change.envFrom: kubectl exec -n <ns> <pod> -- env | sort shows the injected keys.cub unit get --space <space> <config-slug> --web — the AppConfig Unit in the GUI.cub unit livestate --space <space> <config-slug> --web — rendered ConfigMap.cub revision list --space <space> <config-slug> --web — provenance of every config change.https://docs.confighub.com/markdown/guide/app-config.md — canonical walkthrough.references/cub-cli.md — --change-desc / -o mutations / the four Unit views / --where AND-only.references/yaml-patterns.md — confighubplaceholder pattern and Needs/Provides receivers.references/functions-catalog.md — set-string-path / set-int-path / set-bool-path / vet-jsonschema.config-as-data (raw-ConfigMap authoring when app-config isn't a fit), cub-mutate (the bulk / ChangeSet-wrapped path when editing many AppConfig Units together), target-bind (Target + Worker basics), cub-apply (the apply verb), verify-apply (post-apply checks).59ea831
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.