Skip to content

Operate Shared VyOS Image Build Pipeline (HyOps)

Build one canonical VyOS record and publish the state contract consumed by both the Proxmox and Hetzner seed paths.

Module:

  • core/shared/vyos-image-build

Purpose

This is the default product path for VyOS image production.

Use it when you want HybridOps to:

  1. build or validate one canonical record
  2. publish it to object storage
  3. publish a reusable HyOps state contract for downstream seed modules

Use Operate Shared VyOS Image Artifact Contract (HyOps) only when the record already exists and you only need to register it into state.

Default product path

  1. Apply core/shared/vyos-image-build.
  2. Consume its record_state_ref from:
  3. core/onprem/vyos-template-seed
  4. core/hetzner/vyos-image-seed
  5. Let:
  6. platform/onprem/vyos-edge
  7. org/hetzner/vyos-edge-foundation consume the resulting template/image state.

This keeps the platform DRY:

  • build once
  • publish once
  • seed many

Authoritative implementation note:

  • the canonical VyOS cloud-init helper is tools/build/vyos/assets/cc_vyos.py
  • packaged build and seed paths must consume that helper instead of carrying their own divergent copies

Recommended published outputs:

  • Proxmox/default record: versioned public qcow2
  • Hetzner import record: versioned public raw.xz

Why:

  • Proxmox template seeding is cleanest from a qcow2
  • Hetzner image import is cleanest from a directly reachable raw.xz

The qcow2 auto-wrap path for Hetzner still exists, but it is now a compatibility path only. Use it only when the execution host:

  • has qemu-img
  • is publicly reachable for temporary record serving
  • is intentionally acting as the wrapper/import host

If you want predictable Hetzner imports, publish raw.xz explicitly.

Typical validation

hyops validate --env dev --skip-preflight \
  --module core/shared/vyos-image-build \
  --inputs modules/core/shared/vyos-image-build/examples/inputs.gcs-state.yml

Typical apply

hyops apply --env dev \
  --module core/shared/vyos-image-build \
  --inputs modules/core/shared/vyos-image-build/examples/inputs.gcs-state.yml

Verified steady-state example

Validated on Saturday, March 7, 2026:

  • shared build state: core/shared/vyos-image-build#vyos_default_build
  • record version: 2026.03.03-0027-rolling-r2
  • shared qcow2 URL:
  • https://storage.googleapis.com/hyops-dev-vyos-records-a1/network/vyos/vyos-1.5-2026.03.03-0027-rolling-r2.qcow2
  • Hetzner import URL:
  • https://storage.googleapis.com/hyops-dev-vyos-records-a1/network/vyos/vyos-1.5-2026.03.03-0027-rolling.raw.xz

Verification commands:

jq '.status,.outputs' \
  "$HOME/.hybridops/envs/dev/state/modules/core__shared__vyos-image-build/instances/vyos_default_build.json"

jq '.status,.outputs' \
  "$HOME/.hybridops/envs/dev/state/modules/core__hetzner__vyos-image-seed/latest.json"

jq '.status,.outputs' \
  "$HOME/.hybridops/envs/dev/state/modules/core__onprem__vyos-template-seed/latest.json"

Downstream consumption

Proxmox template seed:

record_state_ref: "core/shared/vyos-image-build#vyos_default_build"
record_key: "vyos-1.5"

Hetzner image seed:

  • default: consume record_state_ref when it already resolves a suitable Hetzner import source
  • recommended override: set a direct public image_source_url to the published raw.xz

Important behavior:

  • record_state_ref is authoritative for downstream seed inputs
  • stale rerun values such as record_url, record_sha256, record_version, image_source_url, and seed_command must not override the upstream record contract
  • image_state_ref is authoritative for org/hetzner/vyos-edge-foundation

Builder posture

  • preferred ISO build method remains vyos-vm-images
  • packaged Packer fallback remains available for controlled/debug use
  • KVM-backed builders are the supported path for ISO/Packer builds
  • tcg is debug-only and must not be treated as the production build path

Fresh-user prerequisites

  1. Provision one object repository, for example:
  2. org/gcp/object-repo#vyos_records
  3. Supply upload credentials outside Terraform state, for example:
  4. HYOPS_VYOS_GCS_SA_JSON
  5. HYOPS_VYOS_GCS_SA_JSON_FILE
  6. Apply the build module once.
  7. Consume the published state contract from the Proxmox and Hetzner seed modules.

Cleanup after failed build attempts

After the good image is verified, clean up obsolete records deliberately.

Safe cleanup targets:

  • superseded Hetzner snapshots that are no longer referenced by current state
  • temporary local staging such as /tmp/hyops-hetzner-seed
  • failed builder scratch disks or wrapper outputs
  • stale Proxmox smoke records from failed pre-fix runs:
  • smoke-* test VMs
  • /var/lib/vz/snippets/hyops-vyos-smoke-*
  • /var/tmp/hyops-vyos-smoke-mnt-*

Before deleting anything, confirm the active state points at the current image/template:

jq '.outputs.image_ref,.outputs.image_description' \
  "$HOME/.hybridops/envs/dev/state/modules/core__hetzner__vyos-image-seed/latest.json"

jq '.outputs.template_vm_id,.outputs.template_name' \
  "$HOME/.hybridops/envs/dev/state/modules/core__onprem__vyos-template-seed/latest.json"

Notes

  • prefer a pinned, mirrored record URL over upstream latest
  • do not publish mutable URLs into the shared contract
  • gs://... is not directly usable by Hetzner rescue; publish/consume the equivalent public https://storage.googleapis.com/... URL
  • keep the shared build module as the default story; treat manual record registration as the exception