Storing and Using Application Configuration
ConfigHub enables application configuration files to be represented in their native formats while supporting standard ConfigHub configuration functionality. Application configuration units can be operated upon by standard ConfigHub functions, filtered using ConfigHub's filter syntax, and managed as variants with upstream/downstream relationships. Values can be resolved across links via the Needs/Provides mechanism. Application configuration units can be rendered and deployed as Kubernetes ConfigMaps, mounted as ConfigMap volumes, or -- in the case of Env files -- injected as environment variables using envFrom.
Supported formats
ConfigHub supports the following ToolchainType values for application configuration:
| ToolchainType | Format | File suffix |
|---|---|---|
AppConfig/YAML |
YAML | .yaml |
AppConfig/JSON |
JSON | .json |
AppConfig/INI |
INI | .ini |
AppConfig/TOML |
TOML | .toml |
AppConfig/Properties |
Java Properties | .properties |
AppConfig/Env |
Environment variables | .env |
AppConfig/Text |
Text | .txt |
Required metadata fields
Each application configuration file must include ConfigHub metadata fields, represented consistently with its format. These fields are stripped when rendering a ConfigMap for use within a Kubernetes workload.
configHub.configName-- Uniquely identifies the configuration file. Required for most ConfigHub functionality. Treated similarly to Kubernetes resource names. Also used to generate the data key in the rendered ConfigMap, with the format's file suffix appended (e.g.,MyApplicationConfig.ini).configHub.configSchema-- Uniquely identifies the set of valid values. Required. Conceptually similar to a Kubernetes resource type (e.g.,apps/v1/Deployment). Can be used with thevet-jsonschemafunction to validate application configuration units.
For AppConfig/Text, these metadata fields are stored as standard YAML frontmatter at the beginning of the file, delimited by ---, with the fields nested under a configHub key (see the Text example below). For all other formats, the fields use the configHub. prefix in the format's native syntax.
Example configuration files
Below are examples showing the same application configuration in each supported format.
YAML (AppConfig/YAML)
configHub:
configName: MyApplicationConfig
configSchema: SimpleApp
app:
features:
- authentication
- logging
name: MyApplication
version: 1.0.0
database:
host: localhost
port: 5432
ssl:
enabled: true
JSON (AppConfig/JSON)
{
"configHub": {
"configName": "MyApplicationConfig",
"configSchema": "SimpleApp"
},
"app": {
"features": ["authentication", "logging"],
"name": "MyApplication",
"version": "1.0.0"
},
"database": {
"host": "localhost",
"port": 5432,
"ssl": {
"enabled": true
}
}
}
TOML (AppConfig/TOML)
[configHub]
configName = "MyApplicationConfig"
configSchema = "SimpleApp"
[app]
features = ["authentication", "logging"]
name = "MyApplication"
version = "1.0.0"
[database]
host = "localhost"
port = 5432
[database.ssl]
enabled = true
INI (AppConfig/INI)
[configHub]
configName = MyApplicationConfig
configSchema = SimpleApp
[app]
features.0 = authentication
features.1 = logging
name = MyApplication
version = 1.0.0
[database]
host = localhost
port = 5432
[database.ssl]
enabled = true
Properties (AppConfig/Properties)
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=true
Env (AppConfig/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=true
Text (AppConfig/Text)
Text files use standard YAML frontmatter (delimited by ---) to store ConfigHub metadata:
---
configHub:
configName: MyApplicationConfig
configSchema: SimpleApp
---
# My Application Config
Valid config options are:
* Database Host
* Database Port
* Database SSL Enabled
Deploying as a Kubernetes ConfigMap
Application configuration units are rendered as Kubernetes ConfigMaps by attaching a render-configmap Invocation as the TransformInvocation on an Upsert Link from a downstream Kubernetes/YAML Unit. Rendering is a normal ConfigHub function that runs as part of resolution, so no Target and no worker are involved, and the same mechanism generalizes to other transformation pipelines.
Two rendering modes are supported:
Immutable mode (default, --immutable true on render-configmap): Each rendering produces an immutable ConfigMap with a unique name generated by appending a content hash to the unit slug (similar to ConfigMaps generated by Kustomize). The most recent ConfigMap has the annotation confighub.com/RenderRevision: Latest. The workload must use confighubplaceholder in its ConfigMap references, and the Needs/Provides mechanism replaces them with the generated name. To bound how many historical ConfigMaps remain in the downstream Unit, register the prune-configmaps function as a Mutation Trigger on the Space (see Pruning old ConfigMaps below).
Mutable mode (--immutable false on render-configmap): Produces a single mutable ConfigMap with a stable, predictable name equal to the unit slug (no hash suffix). The workload can reference the ConfigMap by name directly. The ConfigMap carries a confighub.com/Hash annotation containing the content hash. This annotation is registered as a provided value on the ConfigMap and as a needed value on workload podSpec template annotations, so it is automatically propagated via the Needs/Provides mechanism. When the ConfigMap content changes, the hash annotation on the workload's pod template changes, triggering a rolling update. Mutable mode is simpler to set up but does not preserve old ConfigMap versions during rollouts.
Rendering via an Upsert Link
cub unit create --space my-space my-config app.env --toolchain AppConfig/Env
cub unit create --space my-space my-configmap
cub link create --space my-space - my-configmap my-namespace
# Create the render-configmap Invocation (one per (toolchain, mode) combination).
cub invocation create --space my-space render-env-immutable AppConfig/Env render-configmap --immutable true
# Wire the upstream AppConfig unit to the downstream Kubernetes/YAML unit via Upsert.
cub link create --space my-space --wait - my-configmap my-config \
--update-type Upsert --auto-update \
--transform-invocation my-space/render-env-immutable
For AppConfig/Env envFrom injection (each variable becomes a separate ConfigMap data entry), add --as-key-value true to cub invocation create.
For mutable rendering, use --immutable false instead of --immutable true.
After the link resolves, cub unit data --space my-space my-configmap shows the rendered ConfigMap.
Pruning old ConfigMaps
Because Upsert appends new immutable ConfigMaps to the downstream Unit each time the upstream changes, register prune-configmaps as a Mutation Trigger on the Space (or on the downstream Unit's Space, scoped to ConfigMaps) to bound retention:
cub trigger create --space my-space prune-configmaps Mutation Kubernetes/YAML prune-configmaps 10 \
--where-resource "ConfigHub.ResourceType = 'v1/ConfigMap'"
The retention limit is a function argument, so it is passed positionally (or as -- --revision-history-limit=10), not as a flag on cub trigger create. The resource-type predicate belongs to --where-resource, which filters the resources a Trigger operates on; --where-unit filters Units and has no resource-type attribute to match against.
The trigger groups ConfigMaps by their confighub.com/ResourceNameStableCore annotation, orders each group by confighub.com/RevisionNum, keeps the newest entries (with the newest tagged confighub.com/RenderRevision: Latest), and removes the rest. Mutable ConfigMaps are ignored.
Link the workload to the ConfigMap unit
Link the unit containing your Kubernetes workload (e.g., a Deployment) to the ConfigMap unit:
cub link create --space my-space - my-deployment my-configmap
Immutable mode workload configuration
In immutable mode, the workload should use confighubplaceholder in its ConfigMap references. Needs/Provides will replace the placeholder with the generated ConfigMap name. You may optionally use --where-resource on the link to match only the most recently generated ConfigMap:
cub link create --space my-space - my-deployment my-configmap --where-resource "metadata.annotations.confighub~1com/RenderRevision = 'Latest'"
Note:
~1escapes the dots within a single path segment (an encoding inspired by JSON Pointer), so that they are not read as path separators. The annotation keyconfighub.com/RenderRevisiontherefore appears asconfighub~1com/RenderRevision: the dot inconfighub.comis escaped, while the/is left alone.
spec:
template:
spec:
containers:
- name: nginx
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/app.properties
subPath: app.properties
volumes:
- name: config-volume
configMap:
name: confighubplaceholder
When the workload unit is resolved, confighubplaceholder will be replaced with the name of the latest rendered ConfigMap.
Mutable mode workload configuration
In mutable mode, the ConfigMap has a stable name equal to the unit slug, so you can reference it directly. Add a confighub.com/Hash annotation with confighubplaceholder to the pod template; Needs/Provides will replace it with the content hash, triggering a rolling update whenever the ConfigMap changes:
spec:
template:
metadata:
annotations:
confighub.com/Hash: confighubplaceholder
spec:
containers:
- name: nginx
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/app.properties
subPath: app.properties
volumes:
- name: config-volume
configMap:
name: my-config
Environment variable injection using envFrom
For both modes, environment variable injection works the same way (use confighubplaceholder in immutable mode or the stable name in mutable mode):
spec:
template:
spec:
containers:
- name: nginx
envFrom:
- configMapRef:
name: confighubplaceholder
When a workload references multiple ConfigMaps, each ConfigMap's hash is propagated separately. Combining multiple hashes into a single annotation is not yet supported: Needs/Provides has one registered
confighub.com/Hashpath per pod template to propagate into. A TransformPaths Link is not subject to this, because it writes to a path you name rather than to the registered one, so each ConfigMap can be given an annotation of its own.
Rolling a workload when editing a ConfigMap directly
The modes above render a ConfigMap from an AppConfig/* Unit. If instead you author the ConfigMap as ordinary Kubernetes/YAML and want to keep editing it directly, you can still have workloads roll when it changes, using a TransformPaths Link and the get-hash function.
get-hash returns the hash that set-hash would store, without storing it. As an UpstreamGetter it runs against the upstream ConfigMap while the Link resolves, and a DownstreamPaths entry writes the result into the workload's pod template — which is what makes Kubernetes restart the pods:
cat <<'EOF' | cub link create --space my-space app-config-hash my-deployment my-configmap \
--update-type TransformPaths --auto-update --from-stdin
UpstreamGetters:
- Name: configHash
FunctionInvocation:
FunctionName: get-hash
Arguments:
- Value: data
DownstreamPaths:
- Path: spec.template.metadata.annotations.confighub~1com/hash~1my-config
Resource:
ResourceName: default/my-deployment
ResourceType: apps/v1/Deployment
Expression: "{{.Params.configHash}}"
Evaluator: template
Parameters: [configHash]
DataType: string
EOF
Name the annotation after the ConfigMap it hashes, as above, rather than reusing confighub.com/Hash. The confighub.com/Hash annotation is reserved for NeedsProvides.