c9s
Core Concepts

Node profiles

Reusable direct-workload Kubernetes policy referenced explicitly by network Nodes.

A NodeProfile contains reusable Kubernetes policy for the direct device workload that realizes one or more Nodes. It keeps Kubernetes deployment concerns separate from the containerlab-shaped Node payload.

Typical profile settings include:

  • CPU and memory for a Node's primary application container
  • scheduling, tolerations, and affinity rules
  • service exposure
  • Kubernetes image pull policy and pull Secrets
  • status probes
  • persistence
  • management overlay address allocation

Device privilege, capabilities, and devices are never profile policy: they come from the imported containerlab package's plan.

Explicit references

A Node references at most one NodeProfile in the same namespace:

apiVersion: c9s.run/v1alpha1
kind: NodeProfile
metadata:
  name: lab-policy
spec:
  resources:
    requests:
      memory: 4Gi
      cpu: "2"
  statusProbes:
    enabled: true
---
apiVersion: c9s.run/v1alpha1
kind: Node
metadata:
  name: srl1
spec:
  profileRef:
    name: lab-policy
  kind: nokia_srlinux
  image: ghcr.io/nokia/srlinux:26.3

Profiles are not selected by labels and are not merged into inheritance chains. Fields set on the referenced profile take precedence over supported global Config defaults; omitted fields use their built-in defaults or, where available, Config resolution. Service exposure uses the built-in LoadBalancer default because Config has no exposure-mode field. Set spec.expose.exposeType to ClusterIP, Headless, or None to override it.

The spec.scheduling.affinity field accepts the native Kubernetes node affinity, pod affinity, and pod anti-affinity structure. A single NodeProfile can be referenced by multiple Nodes, so the same affinity policy can be reused across direct device Pods. For a Topology, configure the equivalent field at spec.deployment.scheduling.affinity; the Topology controller copies it into the generated NodeProfile.

c9s considers a Node ready only when every device application container representing it is running and passes its Kubernetes probes: readiness behavior declared by the imported containerlab plan, and any image-defined OCI healthcheck or Node healthcheck contract merged over it, translate into container startup and readiness probes. When statusProbes.enabled is true, optional TCP or SSH probe configuration adds an application-level requirement executed inside the device container; c9s does not infer ports, credentials, or behavior from a containerlab kind or image name.

For a kind whose imported plan declares no readiness behavior and an image without a healthcheck, this generic signal is intentionally process-level: a running network OS may still be booting services or converging protocols. Declare a healthcheck, define one in the image, or configure an explicit TCP or SSH probe when application-level readiness is required.

SSH probe credentials are stored in a Secret that c9s creates for the Node; the probe workload reads the password from that Secret. The planner finishes its input and plan before resolving probe credentials. If the same bytes already appear in the declared topology or generated plan, such as the Cumulus VX default password included by containerlab, c9s does not treat that coincidence as a leaked probe password. Other Secret values remain screened from plan artifacts. A refused plan is reported on the Node as PlanApplied=False with reason PlanRejected and a Warning event; the last applied workload keeps running.

If a Node names a profile that does not exist, c9s does not silently fall back to global defaults. The Node remains unrealized until that explicit reference resolves.

Profiles generated from Topology

The Topology compiler normally creates one shared NodeProfile and references it from every generated Node. A Node with distinct resource policy receives a complete dedicated profile rather than a partial child profile.

On this page