Skip to content
Install the CLI

Tutorial 08

Basic dependency resolution and namespace boundaries

In tutorial 06, backend and gateway lived in the same namespace (tutorial-06), so Kix resolved the backend dependency automatically by matching the instance name within that namespace.

In real clusters, services often live in different namespaces: shared backends or data stores might live in a platform namespace while user-facing gateways live in an app namespace. Kix never guesses across namespace boundaries. When a dependency crosses a namespace boundary, you wire it explicitly in cluster.nix using deps and ref.

Why Cross-Namespace Resolution Is Explicit

Section titled “Why Cross-Namespace Resolution Is Explicit”

When a package function asks for a dependency such as backend:

build = { self, scope, kix, backend, ... }: { ... };

Kix resolves backend inside the consumer’s namespace using a strict resolution order:

  1. Explicit deps.backend on the instance in cluster.nix.
  2. Same-namespace match: an instance in the consumer’s namespace whose name, aliases, or package meta.defaultAliases is backend.
  3. Cluster-wide match: an instance in another namespace whose package serves the whole cluster (meta.scope = "cluster", a role in meta.roles, or a cluster: conflict; kix.mkImport stand-ins count). Several matches fail with a deps line for each.
  4. Auto-instantiated catalog package from availablePackages.backend (into the consumer’s namespace, or autoNamespace), or an optional = true instance named backend that Kix enables when another instance depends on it. Either binds from any namespace, because the cluster definition chose where it lives.
  5. Default value (backend ? null) if the package marks the dependency optional; otherwise an error.

Notice that step 2 stays in the consumer’s own namespace. Step 3 reaches another namespace only for a package that serves the whole cluster (such as an operator or an ingress controller), and step 4 reaches another namespace only when the cluster definition placed the instance there. If two namespaces both define an instance named backend, or if backend lives in another namespace and does not serve the whole cluster, Kix will not guess which one you meant. Crossing a namespace boundary is an architectural decision, so Kix makes it visible in cluster.nix.

Open tutorials/08-namespace-deps/cluster.nix. This cluster defines two namespaces:

NamespaceInstancePackageRole
platformbackendpackages.echo-serverShared backend service
appgatewaylocal reverse-proxy-package.nixFrontend reverse proxy

Notice that inside kix.buildCluster, the cluster module in modules is written as a function that accepts ref (while the top-level file header remains { kix, packages }:):

tutorials/08-namespace-deps/cluster.nix
{ kix, packages }:
let
reverseProxyPackage = import ./reverse-proxy-package.nix;
in
kix.buildCluster {
name = "08-namespace-deps";
modules = [
kix.flavors.kind
(
{ ref, ... }:
{
# namespaces and instances ...
}
)
];
}

View source on GitHub ↗

ref is Kix’s cluster-wide instance reference table, passed to cluster modules and indexed by ref.<namespace>.<instance>.

Inside instances.app, the gateway instance connects its backend dependency to ref.platform.backend:

tutorials/08-namespace-deps/cluster.nix
gateway = {
package = reverseProxyPackage;
# Explicit cross-namespace wiring keeps the boundary
# crossing visible at the call site.
deps.backend = ref.platform.backend;
};

View source on GitHub ↗

Look at what changed and what stayed the same:

  • reverse-proxy-package.nix did not change at all. It still asks for backend in build and still writes proxy_pass http://${backend.out.fqdn}:${backend.out.port}/;.
  • cluster.nix added one line: deps.backend = ref.platform.backend;.
  • Because backend is now in the platform namespace, backend.out.fqdn automatically evaluates to backend.platform.svc.cluster.local, updating the gateway’s nginx configuration without touching package code.

Check And Inspect The Cross-Namespace Graph

Section titled “Check And Inspect The Cross-Namespace Graph”

Evaluate the cluster and print its dependency tree:

kix-examples/
❱ kix check 08-namespace-deps
❱ kix graph 08-namespace-deps --format tree

In the dependency tree output, look under Namespace/platform:

Namespace/platform
├── Service/backend@platform
│ ├── ConfigMap/gateway@app
│ │ └── Deployment/gateway@app
│ │ └── Service/gateway@app
│ │ └── PackageInstance/gateway@app

Even though backend and gateway are in different namespaces, the dependency graph connects Service/backend@platform directly to ConfigMap/gateway@app and Deployment/gateway@app. When you run kix deploy 08-namespace-deps, Kix creates both namespaces, deploys backend@platform first, waits for it to pass readiness, and only then rolls out gateway@app.

Deploy the cluster to your local kind environment:

kix-examples/
❱ kix deploy 08-namespace-deps

Port-forward to gateway in the app namespace. Because gateway is the only instance with that name in the cluster, kix pf finds it directly:

kix-examples/
❱ kix pf 08-namespace-deps gateway 8080:80

In another terminal, curl the gateway:

❱ curl http://localhost:8080/
Hello from the platform-owned backend!

Stop the port-forward with Ctrl-C.

Comment out deps.backend = ref.platform.backend; in tutorials/08-namespace-deps/cluster.nix:

tutorials/08-namespace-deps/cluster.nix
gateway = {
package = reverseProxyPackage;
# deps.backend = ref.platform.backend;
};

Now run kix check 08-namespace-deps:

kix-examples/
❱ kix check 08-namespace-deps

Evaluation fails immediately and tells you that gateway in namespace app requires a dependency named backend, and no instance named backend exists in app.

Uncomment deps.backend = ref.platform.backend; so the example is back in its working state.

You split a dependency across two namespaces without modifying the package:

  • automatic name matching only resolves dependencies inside the same namespace
  • cross-namespace dependencies must be wired explicitly in cluster.nix using deps.<arg> = ref.<namespace>.<instance>
  • backend.out.fqdn always includes the provider’s namespace, so cross-namespace DNS names stay accurate automatically
  • kix graph and kix deploy track dependencies across namespaces just like same-namespace dependencies

In the next tutorial, you will add typed configuration options to the reverse-proxy package so cluster files can customize its routing path and body-size limits with compile-time validation.