Skip to content

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 the vet-jsonschema function 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.

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 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: ~1 escapes the dots within a single path segment (an encoding inspired by JSON Pointer), so that they are not read as path separators. The annotation key confighub.com/RenderRevision therefore appears as confighub~1com/RenderRevision: the dot in confighub.com is 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/Hash path 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.