Skip to content

Operate Shared VyOS Image Build Pipeline (HyOps)

Build one canonical VyOS image 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 image
  2. publish it to object storage
  3. publish a reusable HyOps state contract for downstream seed modules

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

Default product path

  1. Apply core/shared/vyos-image-build.
  2. Consume its artifact_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 output:

  • one versioned qcow2 artifact published through core/shared/vyos-image-build

Why:

  • Proxmox template seeding consumes the qcow2 directly
  • Hetzner can consume the same qcow2 through the built-in wrapper path

The Hetzner wrapper path still requires an execution host that:

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

If you want to bypass the wrapper on purpose, publish a direct raw.xz artifact and set image_source_url explicitly for the Hetzner seed module.

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 Sunday, March 16, 2026:

  • shared build state: core/shared/vyos-image-build#vyos_default_build
  • artifact version: 2026.03.03-0027-rolling-r3
  • shared qcow2 URL:
  • https://storage.googleapis.com/hyops-dev-vyos-artifacts-gcp03-a1/network/vyos/vyos-1.5-2026.03.03-0027-rolling-r3.qcow2
  • on-prem template seed state: core/onprem/vyos-template-seed#vyos_template
  • template_source_url now tracks the same shared qcow2 artifact
  • Hetzner seed state can consume the same shared qcow2 artifact through the built-in wrapper path when required_env includes HYOPS_VYOS_GCS_SA_JSON

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:

artifact_state_ref: "core/shared/vyos-image-build#vyos_default_build"
artifact_key: "vyos-1.5"

Hetzner image seed:

  • default: consume artifact_state_ref and declare HYOPS_VYOS_GCS_SA_JSON in required_env when the shared artifact is private GCS-backed
  • optional override: set a direct public image_source_url to a published raw.xz when you want to bypass the wrapper

Important behavior:

  • artifact_state_ref is authoritative for downstream seed inputs
  • when that state resolves to a private GCS-backed shared artifact, the consuming seed module must also declare HYOPS_VYOS_GCS_SA_JSON (or HYOPS_VYOS_GCS_SA_JSON_FILE) in inputs.required_env
  • stale rerun values such as artifact_url, artifact_sha256, artifact_version, image_source_url, and seed_command must not override the upstream artifact 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_artifacts
  3. Supply upload credentials outside Terraform state, for example:
  4. HYOPS_VYOS_GCS_SA_JSON
  5. HYOPS_VYOS_GCS_SA_JSON_FILE
  6. Declare the selected credential env key in inputs.required_env. The standard vault-backed path uses HYOPS_VYOS_GCS_SA_JSON.
  7. Apply the build module once.
  8. 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 artifacts 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 artifacts 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 artifact URL over upstream latest
  • do not publish mutable URLs into the shared contract
  • gs://... is not directly usable by Hetzner rescue; the HyOps wrapper downloads it first on the execution host and then exposes a temporary public raw image URL for Hetzner import
  • keep the shared build module as the default story; treat manual artifact registration as the exception