Skip to content

Compose multi-team clusters with `mergeFragments`

Use kix.mergeFragments when different teams maintain parts of one cluster’s namespace and instance trees. Each fragment remains a normal Nix attrset, so it can live beside the code its team owns.

A fragment can contribute namespaces, instances, or both. This platform fragment adds policy for apps and owns a service in platform:

how-to/composition/platform-fragment.nix (L7–L15)
namespaces.apps = {
labels."platform.example.com/tier" = "application";
annotations."platform.example.com/owner" = "platform";
};
instances.platform.status-api = {
package = packages.echo-server;
config.message = "Platform status";
};

View source on GitHub ↗

An application fragment can add labels to the same namespace and declare its own instances:

how-to/composition/storefront-fragment.nix (L7–L12)
namespaces.apps.labels."app.kubernetes.io/part-of" = "storefront";
instances.apps.storefront = {
package = packages.echo-server;
config.message = "Storefront ready";
};

View source on GitHub ↗

Import each fragment into the cluster file with any arguments it needs. In this example, both imports receive packages:

how-to/composition/fragments-cluster.nix
platformFragment = import ./platform-fragment.nix { inherit packages; };
storefrontFragment = import ./storefront-fragment.nix { inherit packages; };

Pass the fragments in a list. Use namespacePolicies for policy owned by the cluster definition itself:

how-to/composition/fragments-cluster.nix (L14–L22)
composition = kix.mergeFragments {
fragments = [
platformFragment
storefrontFragment
];
# Policies applied by the cluster owner merge with the team fragments.
namespacePolicies.apps.labels."platform.example.com/managed" = "true";
};

View source on GitHub ↗

Namespace attrsets merge recursively. Two fragments may add different labels, annotations, or quota fields to the same namespace. They may not set the same leaf, even to the same value. Kix treats any duplicate leaf as a namespace policy conflict.

namespacePolicies uses the same merge. Cluster-level policy can add leaves, but it cannot override or repeat a leaf set by a fragment.

Instance ownership is stricter. Defining the same instance name in the same namespace from two fragments is always an error.

Assign both outputs inside a cluster module:

how-to/composition/fragments-cluster.nix (L32–L33)
namespaces = composition.namespaces;
instances = composition.instances { };

View source on GitHub ↗

composition.instances is a function so fragments may calculate their instance tree from shared arguments. Pass those arguments in place of { } when a fragment’s instances value is a function.

Evaluate the composed cluster:

Run in kix-examples/
❱ kix check how-to-fragments
 TOOL         RESULT  DETAILS                                                       
 eval         pass    15 manifests evaluated                                        
 kubeconform  pass    skipped (this validation tool is not yet integrated with Kix) 
 pluto        pass    skipped (this validation tool is not yet integrated with Kix) 
 kyverno      pass    skipped (this validation tool is not yet integrated with Kix) 
 scorecard    pass    0 errors, 4 warnings, 2 info

Inspecting the generated apps Namespace confirms that labels and annotations from both teams and the cluster policy were retained:

Run in kix-examples/ Output excerpt
❱ kix build how-to-fragments --output json Show output
{
  "kind": "Namespace",
  "metadata": {
    "name": "apps",
    "labels": {
      "app.kubernetes.io/managed-by": "kix",
      "app.kubernetes.io/part-of": "storefront",
      "kubernetes.io/metadata.name": "apps",
      "platform.example.com/managed": "true",
      "platform.example.com/tier": "application"
    },
    "annotations": {
      "kix.run/identity-hash": "y2nm039ddnq7w89q933whl2nq8z9xls0",
      "kix.run/package": "_cluster",
      "kix.run/package-namespace": "_cluster",
      "platform.example.com/owner": "platform"
    }
  }
}

Keep settings with a single owner where possible. The conflict errors are most useful when overlapping ownership is accidental.