Subtitle: Mastering Declarative Configuration Options
If you’ve spent any time operating Kubernetes in production, you’ve hit the Wall of YAML. Helm charts nested inside other Helm charts, Kustomize overlays overriding overlays, and a sea of copy-pasted boilerplate that slowly drifts across dev, staging, and production environments.
Most platform engineering teams try to solve this with more template processing, leading to the infamous "Kubernetes configuration tax" where engineers spend more time writing string-replacement logic than writing core application code.
In this first part of our series, we'll dive deep into CUE (Configure, Unify, Execute) as a modern, type-safe configuration kernel. We'll explore its powerful declarative options, validation mathematics, and 13 real-world design patterns that demonstrate how to move away from string-replacement text hacks towards model-driven topologies.
CUE Features We Use
Before we delve into the mathematical design, it is helpful to visualize the powerful CUE features utilized within a modern declarative configuration engine:
| FEATURE | WHAT IT GIVES US | WHERE WE USE IT |
|---|---|---|
Definitions#Name: {...} | Typed schemas validated at eval | #App, #CeSite, #StackDns, #Topology |
Unification(#A & #B) | Merge structs with type-safe constraint | #CeSite & #StackDns context merge into apps |
Defaults(*value | type) | Overridable values; consumer narrows only | *"kafka:9092" | string; consumer sets concrete |
String interpolation"\(expr)" | Computed fields from sibling values | Fqdn: "\(AppName).\(ClusterName).\(Suffix)" |
Pattern constraints[pattern]: {...} | Schema for dynamic keys; apply to matching fields | [=~"^dns-\\d+$"]: {...}; topology CE instances |
Optional fieldsname?: type | Omit from export when unset — renderapp injects | AppInstance?: string, TenantName?: string |
@embed(file=...) | Import YAML into CUE at eval time — no codegen | domain/env/params.yaml, domain/net/ (planned) |
Disjunctions(a | b | c) | Constrained enums validated at eval | flavor: *"prod" | "test"; site type constraints |
Dynamic lookupstruct[key] | Index struct by field value at eval time | _flavor.sizing[flavor.sizing] |
@stack(dns)CUE attributes | Metadata attributes travel through unification | Build-time prefix tag; future: K8s labels |
OCI module registrycue.dev/x/... | Versioned upstream schemas & importable dependencies | cue.dev/x/k8s.io; ArgoCD, Kyverno (planned) |
cue export openapi | Bidirectional schema (CUE ↔ OAS/JSON Schema) | Generate OpenAPI docs; Import Netbox OAS |
cue cmd(tool/exec, tool/http) | CUE tool commands — run scripts/APIs from CUE; pipeline: fetch → validate | fetch-netbox (planned); generate-context (planned); import + output in one |
The Mathematics of Validation: The Turned-T Operator (⊥)
To understand how CUE guarantees security, we must briefly look at its mathematical foundations. Unlike scripting languages that execute sequentially, CUE is based on Lattice Theory. It models types and values within a single, ordered algebraic structure.
In CUE, types are values, and values are types. The universe of possibilities starts at the Top element (⊤—representing all possible, unconstrained data) and terminates at the Bottom element (⊥—represented by a literal "T" turned 180 degrees).
Top ( ⊤ ) <-- Anything / Unconstrained
/ \
string int <-- Type constraints
/ \
"dev" 42 <-- Concrete values
\ /
Bottom ( ⊥ ) <-- Error / logical conflict / "Turned-T"
The Turned-T (⊥) represents a logical contradiction, validation failure, or schema conflict. When we merge two structures using the Unification operator (&), the CUE compiler performs a "meet" operation on the lattice.
Core Lattice Operators in CUE
| Operator / Concept | Lattice Representation | CUE Syntax | Practical Meaning / Behavior | Example | Evaluated Output |
|---|---|---|---|---|---|
Top (⊤) | Highest Element (any value) | _ (underscore) | Completely unconstrained. Any type or value satisfies this. | x: _ | Any valid JSON structure |
Bottom (⊥) | Lowest Element (error) | (Compile error) | Represents a type collision, constraint violation, or logical conflict. | x: string & 42 | ⊥ (Bottom / Compilation Failure) |
| Meet (Unification) | Greatest Lower Bound (GLB) | & | Combines two separate constraints or values into a single, merged contract. | x: string & "dev" | "dev" |
| Join (Disjunction) | Least Upper Bound (LUB) | | | Represents a choice between multiple alternatives (constrained options/enums). | x: "prod" | "test" | "prod" | "test" |
| Subtyping (Constraint) | Relation (A ⊑ B) | : (colon) | Asserts that a value or concrete struct satisfies a schema definition. | x: #App | Validated at build-time |
| Default Choice | Primary preference pointer | *default | type | Defines a default value to be used if no overriding choice is supplied. | x: *"small" | string | "small" (defaults to "small") |
If we attempt to unify a field declared as CpuLimit: #CpuQuantity (e.g., matching a string pattern like "500m") with a conflicting raw integer value 42, the mathematical intersection collapses into ⊥ (Bottom). CUE prevents the compilation from succeeding and flags the exact line of the type collision instantly. By making our configurations mathematically sound, CUE ensures that a path leading to ⊥ is caught at compile-time—long before deployment payloads reach the cluster.
The Power of CUE: 13 Real-World Design Patterns
To understand why CUE is a paradigm shift, let's explore 13 concrete design patterns demonstrating how CUE replaces templating boilerplate with structural, typed logic.
Phase 1: Safe Data Ingestion & Transformation
Pattern 1: Embedding YAML directly into CUE
The CUE compiler can embed shared raw static assets (like parameter maps or base image lists) directly into modules, making it a practical data integration layer.
@extern(embed)
package env
import "mxc/pkg/envsubst"
_params: _ @embed(file="params.yaml")
_images: _ @embed(file="images.yaml")
This is an exceptionally strong pattern for configuration management:
- Keep external source data in YAML when that is the most natural format (e.g., vendor lists, legacy charts).
- Load it directly into CUE.
- Apply type constraints and transformations.
- Export cleanly when done.
Pattern 2: Reconstructing values with internal transformation logic
When raw data is embedded, values arrive as plain strings. If those strings contain environment variables ($VAR) or composite references, you need a way to resolve them safely.
This can be achieved via an internal CUE utility package, mxc/pkg/envsubst/envsubst.cue:
package envsubst
#Resolve: {
#base: _
#override: _
// merge base values and overrides
// normalize them into strings
// resolve composed references into final output
}
This utility is then consumed directly within our defaults definition:
_base: {
for k, v in _params {(k): v}
for k, v in _images {(k): v}
}
_resolved: (envsubst.#Resolve & {#base: _base, #override: {}}).out
Historically, writing this type of typed glue code was a high-friction adoption barrier. With AI-assisted development, however, these robust boilerplate structures can be generated in seconds, giving platform teams type-safe reference lookup engines without manual maintenance overhead.
Phase 2: Modeling Identity & Structure
Pattern 3: A shared application schema with computed fields
Platform engineers typically spread naming conventions, DNS patterns, and ingress rules across dozens of files, wiki pages, and pipelines. CUE consolidates this into a single, shared base schema in mxc/schema/apps.cue:
#App: k8s.#FieldSchemas & {
AppName: string
AppInstance?: string
ClusterName: *env.GC_NAME | string
InternalDomain: *env.FABRIC_DOMAIN | string
Environment: *"$ENV_NAME" | string
TenantName?: string
FqdnSuffix: *InternalDomain | string
Fqdn: "\(AppName).\(ClusterName).\(FqdnSuffix)"
}
This ensures a single, immutable contract:
- One place to enforce naming conventions.
- One place to define operational defaults.
- Every single microservice instantly inherits the identical configuration structure.
Pattern 4: Helper packages for naming and identity
To avoid code duplication, we maintain focused helper packages. For example, mxc/pkg/identity.cue exposes a clean #AppIdentity block:
package mxc
import "mxc/domain/env"
#AppIdentity: {
#instance: string
#cluster: *env.GC_NAME | string
#fqdnSuffix: *env.FABRIC_DOMAIN | string
out: {
AppName: #instance
ClusterName: #cluster
FqdnSuffix: #fqdnSuffix
Fqdn: "\(#instance).\(#cluster).\(#fqdnSuffix)"
}
}
Pattern 5: Thin consumer overrides
One of the most elegant outcomes of this architectural model is how thin the final cluster-instantiation files remain. The shared library owns the schema, defaults, and composition rules. The consumer layer owns only the delta.
Take this snippet for an example application:
package exampleapp
import base "mxc/deploy/exampleapp"
base
appInstance: "example-app-prod1"
exampleApp: {
LogLevel: "debug"
database: name: "prod-db1"
}
This pattern completely prevents environments from drifting away from the core platform template over time.
Phase 3: Topology-Driven Topologies
Pattern 6: Shared deployment schemas
Instead of discovering deployment shapes from folder hierarchies alone, we model deployment topologies explicitly in CUE.
#Context: {
namespace?: string
featureflagsList?: [...string]
}
#App: {
enabled: *true | bool
appName?: string
appInstance?: string
packageSourceOverride?: string
}
#Instance: {
enabled: *true | bool
packageSource: string
flavor?: string
context: *{} | #Context
apps: {[string]: #App & context}
}
CUE topology modeling makes your cluster structurally self-aware. You can programmatically validate that apps are routed only to clusters that actually support their required resources.
Pattern 7: Standalone stack modules
Instead of a single, monolithic configuration repository, CUE supports standalone stack modules. For example, a DNS stack can maintain its own isolated module file:
module: "mxc.io/dns"
language: version: "v0.12.0"
The stack divides its layout into two logical domains:
stack-config.cue: Models pure operational data (DNS zones, IPs, records).stack-platform.cue: Handles deployment targets (ArgoCD or Kluctl configurations).
Phase 4: Declarative Code Generation
Pattern 8: Comprehensions for derived infrastructure data
CUE loop comprehensions allow platform engineers to generate repeated, safe operational definitions without manual copy-paste errors.
_nsProxy: {
ns1: "mxc-dns-0"
ns2: "mxc-dns-1"
}
dnsProxy: {
for key, ns in _nsProxy {
"\(key)": {
name: "mxc-dns-proxy-\(key)"
namespace: ns
publicIpRef: key
originRefs: [for sk, _ in sites {"\(sk)/\(key)"}]
}
}
}
zones: {
for siteKey, zoneUid in _zoneUids {
"\(siteKey)": {
uid: zoneUid
recordNames: _zoneRecords
}
}
}
This single block removes mechanical duplication while guaranteeing 100% declarative outputs.
Phase 5: Schema-Driven Sizing & Validation
Pattern 9: Parameter-surface schema validation
We can validate arbitrary parameters on the service boundary using regex-based key matching in CUE:
#ParamSchema: {
[=~".*[Mm]emoryLimit$"]: #MemoryQuantity
[=~".*[Mm]emoryRequest$"]: #MemoryQuantity
[=~".*[Cc]puLimit$"]: #CpuQuantity
[=~".*[Cc]puRequest$"]: #CpuQuantity
[=~".*[Ss]torageSize$"]: #StorageSize
[=~".*[Rr]eplicas$"]: #ReplicaCount
[string]: #_FieldValue
}
This is incredibly powerful: the schema does not need to know every application field in advance. It automatically applies proper Kubernetes resource quantity constraints simply by matching parameter name suffixes!
Pattern 10: App packages combining schema, defaults, and flavors
By grouping schema, defaults, and flavor overrides together, a single component file contains the entire operational lifecycle:
package kafka
import (
"strings"
stack "mxc/deploy/dns"
"mxc/schema"
)
appName: "kafka"
appInstance: "kafka"
flavor: schema.#FlavorSelector
kafka: stack.#App & #Kafka & {
appName: appInstance
clusterName: stack.clusterName
featureflags: strings.Join(stack.featureflagsList, " ")
namespace: *stack.namespace | _
storageClass: *stack.storageClass | _
}
kafka: _flavor[flavor.tier]
Pattern 11: Flavor-based sizing without file sprawl
Instead of maintaining giant values files for each environment, we encapsulate sizing choices as inherited flavors inside CUE:
#FlavorSizingDefaults: #FlavorSizingMap & {
prod: {}
test: {}
dev1: test
staging: {}
preprod: staging
[string]: {}
}
If we want to configure sizing on Grafana, we declare our resource requirements directly in its definition, selecting the sizing tier inside the topology:
grafana: xc.#App & {
AppName: appInstance
CpuLimit: *"1" | _
MemoryLimit: *"1500Mi" | _
}
grafana: _flavor.sizing[flavor.sizing]
Phase 6: Service Relationships and Image Lifecycles
Pattern 12: Cross-package references instead of external wiring glue
In traditional Helm setups, connecting Service A to Service B's local cluster endpoint requires handwriting complex templates. In CUE, services can import other definitions directly, resolving endpoints at compile-time:
import harbor "mxc/deploy/gitops:harbor"
HarborRegistryEndpoint: *"harbor.\(harbor.harbor.Namespace).svc.cluster.local:443" | string
Pattern 13: Promoting versions safely via floating tags and pinned digests
Deploying to production demands a balance between developer speed and reproducibility. We handle this by separating our version declarations inside CUE:
- Human-facing intent: Declare module dependencies using semantic versions (
1.8.x) or release streams (stable,release-2026-q3). - Machine-facing execution: During the GitOps rendering and validation pipeline, the floating pointer is resolved into a concrete, immutable Image SHA (digest).
Because CUE compiles down to hard data files, the final rendered output contains the pinned digest. This gives developers the flexibility of using moving promotion handles, while guaranteeing that our production deployments are 100% reproducible and immune to upstream tag overwrites.
[!TIP] In the next part of this series, we will explore how these CUE features and design patterns are integrated into the actual MXC GitOps engine to drive our cluster deployments! CUE Configuration Kernel (Part 2/2)