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.
Prerequisites
Section titled “Prerequisites”- Finish the package anatomy tutorial.
- Keep the same
kix-examplescheckout and kind cluster.
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:
1build = { self, scope, kix, backend, ... }: { ... };Kix resolves backend inside the consumer’s namespace using a strict resolution order:
- Explicit
deps.backendon the instance incluster.nix. - Same-namespace match: an instance in the consumer’s namespace whose name,
aliases, or packagemeta.defaultAliasesisbackend. - Cluster-wide match: an instance in another namespace whose package serves the whole cluster (
meta.scope = "cluster", a role inmeta.roles, or acluster:conflict;kix.mkImportstand-ins count). Several matches fail with adepsline for each. - Auto-instantiated catalog package from
availablePackages.backend(into the consumer’s namespace, orautoNamespace), or anoptional = trueinstance namedbackendthat Kix enables when another instance depends on it. Either binds from any namespace, because the cluster definition chose where it lives. - 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.
Inspect 08-namespace-deps
Section titled “Inspect 08-namespace-deps”Open tutorials/08-namespace-deps/cluster.nix. This cluster defines two namespaces:
| Namespace | Instance | Package | Role |
|---|---|---|---|
platform | backend | packages.echo-server | Shared backend service |
app | gateway | local reverse-proxy-package.nix | Frontend 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 }:):
{ kix, packages }:
let reverseProxyPackage = import ./reverse-proxy-package.nix;inkix.buildCluster { name = "08-namespace-deps";
modules = [ kix.flavors.kind
( { ref, ... }: { # namespaces and instances ... } ) ];}ref is Kix’s cluster-wide instance reference table, passed to cluster modules and indexed by ref.<namespace>.<instance>.
Wire deps.backend Across Namespaces
Section titled “Wire deps.backend Across Namespaces”Inside instances.app, the gateway instance connects its backend dependency to ref.platform.backend:
60 gateway = {61 package = reverseProxyPackage;62 # Explicit cross-namespace wiring keeps the boundary63 # crossing visible at the call site.64 deps.backend = ref.platform.backend;65 };Look at what changed and what stayed the same:
reverse-proxy-package.nixdid not change at all. It still asks forbackendinbuildand still writesproxy_pass http://${backend.out.fqdn}:${backend.out.port}/;.cluster.nixadded one line:deps.backend = ref.platform.backend;.- Because
backendis now in theplatformnamespace,backend.out.fqdnautomatically evaluates tobackend.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 check 08-namespace-deps❱ kix graph 08-namespace-deps --format tree In the dependency tree output, look under Namespace/platform:
1Namespace/platform2├── Service/backend@platform3│ ├── ConfigMap/gateway@app4│ │ └── Deployment/gateway@app5│ │ └── Service/gateway@app6│ │ └── PackageInstance/gateway@appEven 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 And Test The Cross-Namespace Proxy
Section titled “Deploy And Test The Cross-Namespace Proxy”Deploy the cluster to your local kind environment:
❱ 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 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.
Try Removing The Explicit deps Line
Section titled “Try Removing The Explicit deps Line”Comment out deps.backend = ref.platform.backend; in tutorials/08-namespace-deps/cluster.nix:
1gateway = {2 package = reverseProxyPackage;3 # deps.backend = ref.platform.backend;4};Now run kix check 08-namespace-deps:
❱ 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.
What You Learned
Section titled “What You Learned”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.nixusingdeps.<arg> = ref.<namespace>.<instance> -
backend.out.fqdnalways includes the provider’s namespace, so cross-namespace DNS names stay accurate automatically -
kix graphandkix deploytrack 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.