Build a modular configuration

Use this guide to split a large configuration into generic components and collect them with $ref when producing a delivery artifact.

Identify reusable concepts

Extract a value when it has a meaning and lifecycle of its own, not merely because it is large. Good component candidates include:

  • runtime or cluster profiles;
  • retry, logging, or observability policies;
  • workflow templates;
  • security policy shapes that do not contain secret values; and
  • organization-wide defaults.

Keep environment-specific values, credentials, and one-off application settings close to the root document. A component is most reusable when its name describes a capability rather than its first consumer. Prefer standard-python-worker to orders-worker-settings.

Choose a predictable layout

For example:

configuration/
├── roots/
│   ├── development.yaml
│   └── production.yaml
└── components/
    ├── clusters.yaml
    ├── runtimes.yaml
    └── workflows/
        └── stage-in.yaml

Treat files under roots/ as entry points and files under components/ as the reusable catalog. Relative references are resolved from the document that contains the reference, so moving a document can change what its relative references target.

Give components stable addresses

A catalog file can expose several named values:

# components/runtimes.yaml
python-worker:
  image: python:3.13-slim
  replicas: 2

node-worker:
  image: node:24-slim
  replicas: 2

Select one with a JSON Pointer fragment:

# roots/production.yaml
services:
  worker:
    runtime:
      $ref: ../components/runtimes.yaml#/python-worker

Use document paths and keys as public interfaces. Renaming either is a breaking change for every root that references it.

Compose components recursively

Referenced components may contain references of their own. This makes it possible to build a small hierarchy:

# components/services.yaml
worker:
  runtime:
    $ref: runtimes.yaml#/python-worker
  telemetry:
    $ref: policies.yaml#/standard-telemetry
# roots/production.yaml
services:
  orders:
    $ref: ../components/services.yaml#/worker

Keep the hierarchy shallow enough that a reviewer can trace the effective value. If understanding one service requires following many files, the component boundaries are too fine-grained.

Collect and validate the root

Bundle each root document in CI:

config-mate configuration/roots/production.yaml \
  --output build/production.yaml

The command exits non-zero if a source cannot be loaded or parsed, a reference cannot be resolved, or the result cannot be written. This catches broken paths and pointers before deployment. Validate the collected artifact with the downstream system's own schema or validation command as a separate step; Config Mate resolves references but does not enforce an application-specific schema.

Do not edit files under build/. Regenerate them from the modular sources so that source and artifact cannot drift apart.

Reference remote catalogs carefully

HTTP, S3, and OCI references let several repositories consume a shared catalog. Pin remote components to an immutable version, digest, tag policy, or versioned path whenever the backing system supports it. An unpinned remote document can change the meaning of an otherwise unchanged root configuration.

Pass transport credentials through the CLI environment variables rather than embedding secrets in a $ref. See Remote inputs and authentication.