Skip to content
Plan HCL schema

Plan HCL schema


title: Plan HCL schema

Plan HCL schema

cloud-boot’s plan is an HCL document. It declares one or more targets, each describing how to obtain a kernel + initrd, plus optional local.* and default_target. The whole document is evaluated at boot time after the plan has been fetched from cloudboot.plan=.

Minimal example

default_target = "primary"

target "primary" {
  index   = "ghcr.io/me/cloud/linux:6.6"
  cmdline = "console=ttyAMA0 ro root=/dev/vda1"
}

Full example

default_target = "primary"

locals {
  registry = "harbor.example.com/boot"
  console  = arch == "arm64" ? "ttyAMA0" : "ttyS0"

  # locals can reference each other but not self.*
  base_args = "console=${local.console} ro"
}

target "primary" {
  version = "6.6"
  label   = "Production Linux ${self.version}"
  index   = "${local.registry}/linux:${self.version}"
  cmdline = "${local.base_args} root=/dev/vda1"
  modpack = "${local.registry}/linux-modules:${self.version}"
}

target "rescue" {
  arch    = "amd64"
  kernel  = "${local.registry}/rescue:latest"
  cmdline = ["${local.base_args}", "single", "rd.break"]
}

target "alma" {
  label = "AlmaLinux 9 on XFS"
  disk = {
    device = "/dev/vda2"
    fs     = "xfs"
    kernel = "/boot/vmlinuz-5.14.0-503.el9_5.x86_64"
    initrd = "/boot/initramfs-5.14.0-503.el9_5.x86_64.img"
  }
  cmdline = "console=ttyS0 ro root=UUID=…"
}

target "proxmox" {
  label = "Proxmox VE on ZFS"
  disk = {
    device          = "/dev/vda3"
    fs              = "zfs"
    dataset         = "rpool/ROOT/pve-1"
    kernel          = "/boot/vmlinuz-6.5.13-1-pve"
    initrd          = "/boot/initrd.img-6.5.13-1-pve"
    luks-passphrase = "${meta.cloudboot.luks_passphrase}"
  }
  cmdline = "root=ZFS=rpool/ROOT/pve-1 boot=zfs"
}

Top-level keys

KeyTypeMeaning
default_targetstringName of the target {} block to use when cloudboot.target= is unset.
locals { … }blockNamed values reusable inside target {}. See scoping.
target "<name>" { … }block(s)One or more boot targets.

target { } attributes

A target can be either OCI-sourced (kernel/initrd pulled from a registry) or disk-sourced (read from a chained distro’s filesystem). The opener picks the mode based on which attributes are present.

AttributeTypeMeaning
labelstringHuman-readable name for the menu. Defaults to name.
versionstringFree-form version string, referenced via self.version.
archstringOptional arch filter — target is hidden on non-matching guests. Accepts amd64, arm64.
cmdlinestring | list(string)Kernel command line. List members are space-joined.
OCI source
indexstringOCI manifest list (multi-arch). cloud-boot picks the matching arch.
kernelstringDirect OCI artifact reference (used when index is absent).
initrdstringDirect OCI artifact reference for the initrd.
modpackstringOptional OCI artifact for matching kernel modules.
Disk source
disk = { device = …, fs = …, kernel = …, initrd = …, dataset = …, luks-passphrase = … }blockAll cloudboot.disk.* cmdline keys, available as plan attributes. See the cmdline reference for semantics.

Scoping

cloud-boot’s HCL evaluator exposes the following scopes:

ScopeAvailable whereContains
archlocals + targetsGuest arch string: amd64 or arm64.
local.*inside target { } and other locals { }Values defined in the top-level locals { } block.
self.*inside the same target { }The target’s own attributes (e.g. self.version). Useful for templating: "${local.registry}/linux:${self.version}".
meta.*inside target { }Parsed metadata-URL JSON (the entire response, not just the cloudboot block). Useful for ${meta.cloudboot.luks_passphrase}.

Multi-arch OCI indexes

When you set index = "..." (not kernel = "..."), cloud-boot issues a HEAD against the OCI ref to get the manifest list, then picks the descriptor whose platform matches the guest. The HCL never needs an arch == … branch for kernel selection — the registry handles it via the manifest’s platform.architecture.

locals { registry = "harbor.example.com/boot" }
target "primary" {
  # one ref for both arm64 and amd64 — cloud-boot picks the right one
  index   = "${local.registry}/linux:6.6"
  cmdline = "console=${arch == "arm64" ? "ttyAMA0" : "ttyS0"}"
}

Validation

Plan validation happens at boot. Errors abort the boot before kexec / reboot(2), so a syntactically broken plan can’t strand the guest in a half-staged state. The validator checks:

  • default_target names a defined target.
  • Every target has either an OCI source (index or kernel) or a disk block, never both.
  • Disk-source fs is one of the supported four.
  • local.* references resolve.
  • self.* references are local to their own target {} block.
  • arch values, when present, are amd64 or arm64.