Skip to content

Provision Hetzner VyOS Edge (HyOps Blueprint)

Use the shipped blueprint ref networking/hetzner-vyos-edge@v1 to build/publish (or reuse) a canonical VyOS artifact, seed or discover the Hetzner image, and provision the default routed Hetzner edge pair.

This is the target-state replacement for the Linux strongSwan/FRR edge path. The shared Hetzner control host remains a separate Linux service host.

Current recommended image path:

  1. publish one canonical versioned qcow2 through core/shared/vyos-image-build
  2. let core/hetzner/vyos-image-seed consume that shared artifact contract
  3. when the shared artifact is private GCS-backed, declare HYOPS_VYOS_GCS_SA_JSON (or HYOPS_VYOS_GCS_SA_JSON_FILE) in required_env
  4. let the built-in Hetzner wrapper convert and expose the temporary raw import artifact
  5. let org/hetzner/vyos-edge-foundation consume image_state_ref

Important first-boot behavior:

  • Hetzner VyOS custom images now perform one intentional first-boot reboot after cloud-init writes the final config.boot.
  • Treat the first boot as image customization and the second boot as the point where routed public/private networking becomes active.
  • Do not treat a short initial SSH gap as image failure unless the node is still unreachable after that reboot window.
  • Hetzner Cloud Networks are routed. The edge private NIC is therefore configured as private_ip/32, with an explicit route to private_network_cidr via the standard network gateway (cidrhost(private_network_cidr, 1)), not as an on-link /24.

Rerun safety:

  • The shared-network and edge-foundation steps now verify live Hetzner state before skipping on state-ok.
  • If the published edge server ids are missing from the Hetzner API, deploy reruns the foundation step instead of trusting stale state.

Inputs

The blueprint is state-first where possible:

  • core/shared/vyos-image-build publishes the canonical VyOS artifact URL contract
  • core/hetzner/vyos-image-seed publishes the custom Hetzner image reference
  • org/hetzner/shared-private-network#shared_network publishes the reusable Hetzner private network contract
  • org/hetzner/vyos-edge-foundation consumes it by image_state_ref

Operator-supplied values still required:

  • object repo state or explicit artifact URL for the build step
  • a public wrapper base URL reachable from Hetzner rescue when using the qcow2 wrapper path
  • SSH public key when HyOps must create the Hetzner SSH key
  • explicit ssh_source_cidrs and ipsec_source_cidrs for the live peer set

Initialize an env-scoped overlay

hyops blueprint init --env dev \
  --ref networking/hetzner-vyos-edge@v1 \
  --dest-name hetzner-vyos-edge.yml

Edit:

  • ~/.hybridops/envs/dev/config/blueprints/hetzner-vyos-edge.yml
  • Replace all CHANGE_ME_* values before preflight or deploy.
  • Set at minimum:
  • CHANGE_ME_HETZNER_NETWORK_ZONE
  • CHANGE_ME_HETZNER_PRIVATE_NETWORK_NAME
  • CHANGE_ME_HETZNER_PRIVATE_NETWORK_CIDR
  • CHANGE_ME_HETZNER_LOCATION
  • CHANGE_ME_HETZNER_HOME_LOCATION
  • CHANGE_ME_HETZNER_SERVER_TYPE
  • CHANGE_ME_HETZNER_SSH_KEY_NAME
  • CHANGE_ME_EDGE_FIREWALL_NAME
  • CHANGE_ME_EDGE_FLOATING_IP_NAME
  • CHANGE_ME_EDGE01_PRIVATE_IP
  • CHANGE_ME_EDGE02_PRIVATE_IP
  • If you already have a seeded Hetzner custom image, set image_ref.
  • The blueprint now defaults to core/shared/vyos-image-build#vyos_default_build.
  • If vyos_artifact_build.inputs.artifact_url is set, HyOps uses it directly.
  • If vyos_artifact_build.inputs.artifact_url is empty, HyOps runs build/publish and uses repo_state_ref to discover bucket backend.
  • vyos_image consumes the resulting shared artifact state by default.
  • The shipped blueprint now provisions or reuses org/hetzner/shared-private-network#shared_network before the edge foundation step.
  • The default routed-edge contract now uses foundation_state_ref: org/hetzner/shared-private-network#shared_network.
  • The default Hetzner path is now state-first: consume artifact_state_ref: core/shared/vyos-image-build#vyos_default_build and let the built-in wrapper handle the qcow2 import path.
  • When that shared artifact is private GCS-backed, declare HYOPS_VYOS_GCS_SA_JSON in vyos_image.inputs.required_env.
  • The execution host for the wrapper path must have qemu-img and must be able to expose the wrapped artifact through a public base URL.
  • Override foundation_state_ref, private_network_name, or private_network_cidr only when you intentionally need a different network ownership model.
  • Set ssh_source_cidrs and ipsec_source_cidrs explicitly so reapply does not widen firewall rules or drift into 0.0.0.0/0 defaults.
  • If your only upstream source is an installer ISO, leave image_source_url empty and provide a custom seed_command that performs the ISO-to-image workflow before snapshot creation.
  • Leave seed_tool: hcloud-upload-image unless you provide a custom seed_command.
  • HyOps fails fast when image_source_url is invalid/unreachable, with guidance to run core/shared/vyos-image-build.
  • A single operator-managed qcow2 URL can still serve both Proxmox and Hetzner when the wrapper preconditions are satisfied. Use a direct raw.xz import URL only when you intentionally want to bypass the wrapper.

If hyops blueprint init --ref networking/hetzner-vyos-edge@v1 says the blueprint is not found under ~/.hybridops/core/app/blueprints/..., your installed HyOps payload is older than the current source tree. Refresh the install, or use the repo-local CLI until the installed payload is updated.

Validate

hyops blueprint preflight --env dev \
  --file "$HOME/.hybridops/envs/dev/config/blueprints/hetzner-vyos-edge.yml"

Deploy

hyops blueprint deploy --env dev \
  --file "$HOME/.hybridops/envs/dev/config/blueprints/hetzner-vyos-edge.yml" \
  --execute

Verify

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

jq '.status,.outputs.edge01_public_ip,.outputs.edge02_public_ip' \
  "$HOME/.hybridops/envs/dev/state/modules/org__hetzner__vyos-edge-foundation/latest.json"

EDGE_A_IP="$(jq -r '.outputs.edge01_public_ip' "$HOME/.hybridops/envs/dev/state/modules/org__hetzner__vyos-edge-foundation/latest.json")"
EDGE_B_IP="$(jq -r '.outputs.edge02_public_ip' "$HOME/.hybridops/envs/dev/state/modules/org__hetzner__vyos-edge-foundation/latest.json")"
ssh -o StrictHostKeyChecking=no "vyos@${EDGE_A_IP}" 'echo HYOPS_HETZNER_EDGE_A_OK'
ssh -o StrictHostKeyChecking=no "vyos@${EDGE_B_IP}" 'echo HYOPS_HETZNER_EDGE_B_OK'

If the edge foundation is reapplied and Hetzner assigns new public IPs, refresh org/gcp/wan-vpn-to-edge before rerunning WAN day-2. The cloud VPN peer IPs must track the current org/hetzner/vyos-edge-foundation state.