Skip to main content
A Node represents a physical or virtual machine managed by a cara-agent instance. Caravanserai uses Nodes as scheduling targets: the Scheduler assigns Projects only to Nodes in the Ready state.
cara-agent self-registers its Node with the control plane on startup using the NODE_NAME environment variable. You only need to write a Node manifest manually when you want to pre-register a node before the agent starts, or to update node configuration such as labels or schedulability.

Schema

Top-level fields

apiVersion
string
required
Must be caravanserai/v1.
kind
string
required
Must be Node.
metadata
object
required
Identity metadata for the resource.
metadata.name
string
required
Unique identifier for the Node within the cluster. This is the name the agent registers under and the name you use in caractrl commands. Typically matches the machine hostname or a descriptive label such as edge-node-01.
metadata.labels
object
Arbitrary key/value pairs for grouping and selection. Common uses include zone or region labels, for example caravanserai.io/zone: ed312.

spec

spec.hostname
string
The OS-level hostname of the machine, for example pve-03 or edge-01.local. Used for informational purposes and audit trails.
spec.unschedulable
boolean
default:"false"
When true, prevents the scheduler from assigning new Projects to this Node. Projects already running on the Node continue to run. Set this field to true before draining a node for maintenance.

Example

node.yaml
apiVersion: caravanserai/v1
kind: Node
metadata:
  name: edge-node-01
  labels:
    zone: hsinchu
spec:
  hostname: edge-01.local
  unschedulable: false

Marking a node unschedulable

Set spec.unschedulable: true to cordon a node. The control plane stops scheduling new Projects onto it while existing workloads continue running.
1

Edit your node manifest

Set unschedulable to true in your manifest:
node.yaml
apiVersion: caravanserai/v1
kind: Node
metadata:
  name: edge-node-01
spec:
  hostname: edge-01.local
  unschedulable: true
2

Apply the change

caractrl apply -f node.yaml
The control plane marks the node unschedulable immediately. The scheduler will not place new Projects on this node until you set unschedulable: false and reapply.
3

Verify the node state

caractrl get nodes
The node remains visible and its existing Projects continue to run. Only new scheduling is blocked.
Setting unschedulable: true does not evict Projects already running on the node. To migrate workloads away, delete and reapply each Project after cordoning the node.

Status fields (read-only)

The following fields are written by the system — by cara-agent (heartbeat data) and the Controller Manager (aggregated state). Do not include them in your manifests.
High-level health summary of the Node. One of:
ValueMeaning
ReadyNode is healthy and accepting work
NotReadyAgent heartbeat has timed out or the node is unhealthy
DrainingNode is being drained; no new Projects are scheduled
Overlay-network connectivity reported by the Headscale/Tailscale integration.
FieldDescription
ipHeadscale-assigned overlay IP address, e.g. 100.64.0.5
dnsNameMagicDNS FQDN for service discovery
modeDirect when a peer-to-peer path exists; DERP otherwise
agentPortTCP port the agent’s HTTP server listens on
throughput.downloadLast measured download speed
throughput.uploadLast measured upload speed
throughput.lastTestTimeTimestamp of the last speed measurement
Resource totals reported by the agent.
FieldDescription
capacityRaw physical resource totals (cpu, memory, disk). Values use Kubernetes quantity format: "4Gi", "2000m".
allocatableCapacity minus system-reserved amounts. The Scheduler uses this to determine available headroom for new Projects.
RFC 3339 timestamp of the most recent heartbeat received from the agent. The control plane uses this to detect unresponsive nodes and transition them to NotReady.
A list of granular observable conditions on the Node. Each condition has:
FieldDescription
typeMachine-readable identifier, e.g. Ready
statusTrue, False, or Unknown
reasonCamelCase word summarising the current status
messageHuman-readable explanation
lastHeartbeatTimeWhen this condition was last sampled
lastTransitionTimeWhen the status last changed