UEFI HTTP Network Boot not working due to missing /dev/ram0

I want to install openSuse Leap Micro 6.1 via the UEFI HTTP network boot with Grub2. My network services with Grub2 are already working and the server is loading the initrd and kernel files, but the boot stops due to the fact that systemd can’t find the root device /dev/ram0:

My Grub2 menu entry is the following:

menuentry "Install openSUSE Leap Micro" --class os --unrestricted $menuentry_id_option 'opensuse-leap-micro-6-1' {
    set gfxpayload=keep
    echo Loading kernel...
    # linux ($root)/boot/x86_64/loader/linux cdinst=1 console=ttyS0,115200 console=tty0 security=selinux selinux=1 quiet systemd.show_status=1 net.ifnames=0  loglevel=0 root=install:CDLABEL=INSTALL
    linux ($root)/images/openSUSE-Leap-Micro/6.1/pxeboot.openSUSE-Leap-Micro.x86_64-6.1.kernel root=/dev/ram0 rd.kiwi.install.pxe rd.kiwi.install.image=http://192.168.2.221/images/openSUSE-Leap-Micro/6.1/openSUSE-Leap-Micro.x86_64-6.1.xz console=ttyS0,115200 console=tty0 security=selinux selinux=1 quiet systemd.show_status=1 net.ifnames=0 ignition.firstboot ignition.config.url=http://192.168.2.221/hosts/de-eve72-cl2k3s1.ign
    echo Loading initrd...
    # initrd ($root)/boot/x86_64/loader/initrd
    initrd ($root)/images/openSUSE-Leap-Micro/6.1/pxeboot.openSUSE-Leap-Micro.x86_64-6.1.initrd
}

I used this Suse documentation for PXE and TFTP boot, but I had to adapt it for UEFI and HTTP boot with Grub2. But as far as I can tell the differences don’t matter as the documentation states that the device /dev/ram0has to be booted. When I enter another device name in Grub2 I will see that device failing, so at least the parameters in Grub2 are used for the boot process.

Any idea what I need to change to get the image booted?

If I delete the root= parameter the manual installation begins as the installer is asking for a installation disk (/dev/sda or /dev/sdb in my case). I tried with this Grub2 configuration:

menuentry "Install openSUSE Leap Micro" --class os --unrestricted $menuentry_id_option 'opensuse-leap-micro-6-1' {
    set gfxpayload=keep
    echo Loading kernel...
    # linux ($root)/boot/x86_64/loader/linux cdinst=1 console=ttyS0,115200 console=tty0 security=selinux selinux=1 quiet systemd.show_status=1 net.ifnames=0  loglevel=0 root=install:CDLABEL=INSTALL
    linux ($root)/images/openSUSE-Leap-Micro/6.1/pxeboot.openSUSE-Leap-Micro.x86_64-6.1.kernel rd.kiwi.install.pxe rd.kiwi.install.image=http://192.168.2.221/files/images/openSUSE-Leap-Micro/6.1/openSUSE-Leap-Micro.x86_64-6.1.xz rd.kiwi.oem.installdevice rd.kiwi.install.pass.bootparam console=ttyS0,115200 console=tty0 security=selinux selinux=1 quiet systemd.show_status=1 net.ifnames=0 ignition.config.url=http://192.168.2.221/hosts/de-eve72-cl2k3s1.ign
    echo Loading initrd...
    # initrd ($root)/boot/x86_64/loader/initrd
    initrd ($root)/images/openSUSE-Leap-Micro/6.1/pxeboot.openSUSE-Leap-Micro.x86_64-6.1.initrd
}

For me it looks like that the parameters are not getting passed to the kiwi installer as it does not try to get the openSUSE-Leap-Micro.x86_64-6.1.xz and de-eve72-cl2k3s1.ign files at all as I can see in the Ninx log files.

After weeks of stupid grinding I got it working!

Let me try to explain how to get it working:

( 192.168.2.221 is the IP address of my server running the Nginx HTTP server)

  1. Create a folder structure like:
my_network_boot
my_network_boot/configs/butane
my_network_boot/files/grub2
my_network_boot/files/hosts/grub2
my_network_boot/files/images
my_network_boot/nginx/conf.d
  1. Create some files:
    my_network_boot/nginx/conf.d/default.conf:
server {
    listen 80;
    listen [::]:80;
	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	types_hash_max_size 2048;
	client_max_body_size 0;
	include /etc/nginx/mime.types;
	default_type application/octet-stream;
	error_log  /var/log/nginx/error.log notice;
	access_log  /var/log/nginx/access.log  main;
	gzip on;
	gzip_disable "msie6";
	include /config/nginx/site-confs/*;
}

my_network_boot/nginx/html as a soft link to the files directory:

ln -s files nginx/html

my_network_boot/compose.yaml:

---
services:
nginx:
    container_name: netboot_nginx
    image: nginx:1.27.4-alpine3.21
    network_mode: host
    ports:
      - 80:80
    volumes:
      - type: bind
        source: "${PWD}/nginx/conf.d"
        target: "/etc/nginx/conf.d"
        read_only: true
      - type: bind
        source: "${PWD}/nginx/html"
        target: "/etc/nginx/html"
        read_only: true
    restart: unless-stopped

my_network_boot/get_files.sh:

#!/usr/bin/env bash

#Define source directory of the Scrtipt
SOURCE_DIR=$(realpath "$(dirname -- "${BASH_SOURCE[0]}")")

# openSUSE Leap Micro 6.1
# https://cdn.opensuse.org/download/distribution/leap-micro/6.1/appliances/
# https://documentation.suse.com/sle-micro/6.1/html/Micro-deployment-pxe/index.html
echo "\n########## openSUSE-Leap-Micro 6.1"
mkdir -p ${SOURCE_DIR}/files/images/openSUSE-Leap-Micro/6.1
wget -O ${SOURCE_DIR}/files/images/openSUSE-Leap-Micro/6.1/openSUSE-Leap-Micro.x86_64-Default-SelfInstall.install.tar -nc https://cdn.opensuse.org/download/distribution/leap-micro/6.1/appliances/openSUSE-Leap-Micro.x86_64-Default-SelfInstall.install.tar
tar xvf ${SOURCE_DIR}/files/images/openSUSE-Leap-Micro/6.1/openSUSE-Leap-Micro.x86_64-Default-SelfInstall.install.tar -C ${SOURCE_DIR}/files/images/openSUSE-Leap-Micro/6.1 --overwrite

my_network_boot/configs/butane/your_hostname.bu.yaml:

variant: fcos
version: 1.4.0
passwd:
  users:
    - name: root
      ssh_authorized_keys:
        - "ssh-rsa AAAAB3NzaC.......b3Z5TXQ== your_comment"
      password_hash: $6$sxfGOnw5tPck1LoS$N.YsNpCFM1MnbmAUGqmmZt/wy.42ugI7K1ZcGBfp4XdufTXxst1.I0u297hCWf.CKWxNwQ9joSgdh7KU96VKH1
    - name: yourUsername
      ssh_authorized_keys:
        - "ssh-rsa AAAAB3NzaC.......b3Z5TXQ== your_comment"
      password_hash: $6$sxfGOn........U96VKH
      home_dir: /home/yourUsername
      groups:
        - wheel
      shell: /bin/bash
storage:
  disks:
    - device: "/dev/disk/by-id/wwn-0x50014ee21264eceb"
      wipe_table: true
      partitions:
        - number: 1
          size_mib: 0
          label: opt
  filesystems:
    - path: /home
      device: "/dev/disk/by-id/wwn-0x5002538d40db1ab0-part3"
      format: btrfs
      wipe_filesystem: false
      with_mount_unit: true
      mount_options:
        - "subvol=/@/home"
  files:
    - path: /etc/NetworkManager/system-connections/enp1s0.nmconnection
      mode: 0600
      contents:
        inline: |
          [connection]
          id=enp1s0
          uuid=b5344841-0f14-4406-b699-c9d70cc198f9
          type=ethernet
          autoconnect-priority=-100
          autoconnect-retries=1
          interface-name=enp1s0
          multi-connect=1

          [ethernet]

          [ipv4]
          address1=192.168.0.5/23
          dns=192.168.0.1;
          gateway=192.168.0.1
          may-fail=false
          method=manual

          [ipv6]
          method=disabled

          [proxy]
systemd:
  units:
    - name: sshd.service
      enabled: true
    - name: cockpit.socket
      enabled: true

my_network_boot/files/hosts/grub2/a8:a1:59:1d:ca:c0 with a file name with the MAC address of the to be network booted device:
(Change the example IP address 192.168.2.221 in the file)
(Change your_hostname.ign)

# opensuse leap micro 6.1

set default="opensuse-leap-micro-6-1"
set timeout=30

if [ x"${feature_menuentry_id}" = xy ]; then
  menuentry_id_option="--id"
else
  menuentry_id_option=""
fi

menuentry "Boot from Hard Disk" --class os --unrestricted $menuentry_id_option 'boot-from-hard-disk' {
    exit
}
menuentry "Install openSUSE Leap Micro" --class os --unrestricted $menuentry_id_option 'opensuse-leap-micro-6-1' {
    echo Loading kernel...
    linux ($root)/images/openSUSE-Leap-Micro/6.1/pxeboot.openSUSE-Leap-Micro.x86_64-6.1.kernel rd.kiwi.oem.installdevice=/dev/disk/by-id/wwn-0x5002538d40db1ab0 rd.kiwi.install.pass.bootparam rd.kiwi.install.pxe rd.kiwi.install.image=http://192.168.2.221/images/openSUSE-Leap-Micro/6.1/openSUSE-Leap-Micro.x86_64-6.1.xz ignition.config.url=http://192.168.2.221/hosts/your_hostname.ign
    echo Loading initrd...
    initrd ($root)/images/openSUSE-Leap-Micro/6.1/pxeboot.openSUSE-Leap-Micro.x86_64-6.1.initrd
}

my_network_boot/files/grub2/grub.cfg

if [ -s /hosts/grub2/${net_default_mac} ]; then
   source /hosts/grub2/${net_default_mac}
fi
  1. Create the Grub2 netboot directories with grub2-mknetdir:
    grub2-mknetdir(1) — grub2-common
cd files
sudo grub2-mknetdir --net-directory=./ --subdir=grub2
cd ..
  1. Fix the owner:
sudo chown yourUsername:yourUserngroup -R files
  1. Create the Grub2 images with grub2-mkimage
    grub2-mkimage(1) — grub2-common
    (Change the example IP address 192.168.2.221 in the command)
cd files
sudo grub2-mkimage -O x86_64-efi --output grub2/x86_64-efi/core.efi --prefix '(http,192.168.2.221)/grub2' btrfs tftp normal ls echo minicmd halt net reboot http linux bsd zfs video efi_gop efi_uga efifwsetup efinet efitextmode
cd ..
  1. Start the container using Docker or Podman:
podman compose up -d --build

Logs can be checked with:

podman logs -f netboot_nginx
  1. Confige your already existing DHCP server, in this case it’s a config for ISC DHCP on an OPNsense,
    /usr/local/etc/dhcpd.opnsense.d/http-network-boot.conf:
    (Change the example IP address 192.168.2.221 in the file)
class "httpclients" {
    option vendor-class-identifier "HTTPClient";
    match if substring (option vendor-class-identifier, 0, 10) = "HTTPClient";
    filename "http://192.168.2.221/grub2/x86_64-efi/core.efi";
}
  1. Generate the ignition config out of the butane config:
    (use podman or docker)
podman run --interactive --rm quay.io/coreos/butane:release --pretty --strict < configs/butane/your_hostname.bu.yaml > files/hosts/your_hostname.ign
  1. Now you can boot your device via UEFI HTTP IPv4
  2. Pray! :grinning:
2 Likes