How to create a UBOS development VM for VirtualBox on any x86_64 system

/docs/linux/developer/create-developer-vm/x86_64-virtualbox/

Note

In our experience, VirtualBox on ARM is currently not mature enough yet to be used, so use this recipe for on x86_64 hosts only.

Install VirtualBox

  1. Download and install VirtualBox if you have not done so already.

Obtain an Arch Linux image

  1. Download an ISO from a mirror listed on https://archlinux.org/download/ (e.g. archlinux-2024.11.01-x86_64.iso)

Run the Arch Linux ISO as the virtual machine to create the development VM with

  1. In VirtualBox, create a new virtual machine:

    • Click “New”.

    • In section “Name and Operating System”:

      • Enter name: “ubosdev_x86_64-vbox-YYYYMMDD-I” where YYYYMMDD is the date, and I is a number starting with 1. (Don’t add a UBOS channel to the name, as this is Arch.)

      • ISO Image: select the ISO image you downloaded.

      • Type: select “Linux”.

      • Subtype: select “ArchLinux”

      • Version: select “Arch Linux (64 bit)”.

    • In section “Hardware”:

      • Base memory 4096MB.

      • 2 CPUs.

      • Leave “Enable EFI (special OSes only)” unchecked.

    • Section “Hard Disk”:

      • “Create a Virtual Hard Disk Now”.

      • with 60 GB. This only means the disk may grow up to 60GB, not that it starts out that large.

      • Disk type: “VDI (VirtualBox Disk Image)”

      • Do not check “Pre-allocate Full Size”

    • “Finish”

  2. Click “Start” to start the virtual machine, accept the defaults in the boot loader (or just wait) and wait until the boot sequence ends and the root shell appears.

Install Arch on the empty disk and configure it

  1. Update the bootstrap VM and install some packages we need:

    # pacman -Sy
    # pacman -S archlinux-keyring
    # pacman -Su
    # pacman -S btrfs-progs gptfdisk parted dosfstools arch-install-scripts vi
    
  2. Zero out the first bytes on the disk for extra robustness:

    # dd if=/dev/zero of=/dev/sda bs=1M count=8 conv=notrunc
    
  3. Clear the partition table:

    # sgdisk --clear /dev/sda
    
  4. Create the partitions (UEFI, /boot and /) and change them to the right types:

    # sgdisk --new=1::+1M /dev/sda
    # sgdisk --new=2::+512M /dev/sda
    # sgdisk --new=3:: /dev/sda
    # sgdisk --typecode=1:EF02 /dev/sda
    # sgdisk --typecode=2:EF00 /dev/sda
    
  5. Make sure changes are in effect:

    # sync
    # partprobe /dev/sda
    
  6. Create filesystems for partitions other than the UEFI partition:

    # mkfs.vfat  /dev/sda2
    # mkfs.btrfs /dev/sda3
    
  7. Mount the partitions so we can install:

    # mount /dev/sda3 /mnt
    # mkdir /mnt/boot
    # mount /dev/sda2 /mnt/boot
    
  8. Perform the actual install:

    # pacstrap /mnt base
    
  9. Generate the right fstab:

    # genfstab -U -p /mnt >> /mnt/etc/fstab
    
  10. Chroot into your future root disk and continue the installation:

    # arch-chroot /mnt
    
    1. Add the UBOS keyring so we can install our own packages:

      # curl -O https://depot.ubosfiles.net/yellow/$(uname -m)/os/ubos-keyring-0.9-2-any.pkg.tar.zst
      # pacman -U ubos-keyring-0.9-2-any.pkg.tar.zst
      # rm ubos-keyring-0.9-2-any.pkg.tar.zst
      
    2. Add the UBOS tools repo:

      # echo '' >> /etc/pacman.conf
      # echo '[ubos-tools-arch]' >> /etc/pacman.conf
      # echo 'Server = https://depot.ubosfiles.net/yellow/$arch/ubos-tools-arch' >> /etc/pacman.conf'
      
    3. Install more packages:

      # pacman -Sy
      # pacman -S sudo vim btrfs-progs virtualbox-guest-utils \
        gdm gnome-console gnome-control-center gnome-session gnome-settings-daemon \
        gnome-shell gnome-keyring nautilus \
        ubos-tools-arch
      

      If asked which alternatives to install, choose the defaults.

    4. Check that the auto-generated content of /etc/pacman.d/mirrorlist makes sense. Edit accordingly.

    5. Create a ramdisk:

      # mkinitcpio -p linux
      
    6. Configure the boot loader:

      # bootctl --path /boot install
      
    7. Install a locale:

      # perl -pi -e 's!#en_US.UTF-8 UTF-8!en_US.UTF-8 UTF-8!' /etc/locale.gen
      # locale-gen
      
    8. Set up networking:

      # echo '[Match]' > /etc/systemd/network/wired.network
      # echo 'Name=en*' >> /etc/systemd/network/wired.network
      # echo '' >> /etc/systemd/network/wired.network
      # echo '[Network]' >> /etc/systemd/network/wired.network
      # echo 'DHCP=ipv4' >> /etc/systemd/network/wired.network
      # echo 'IPForwarding=1' >> /etc/systemd/network/wired.network
      
      # systemctl enable systemd-networkd systemd-resolved systemd-timesyncd
      
    9. Create a user with the right permissions and no password:

      # useradd -m ubosdev
      # chmod 755 ~ubosdev
      # passwd -d ubosdev
      # echo ubosdev ALL = NOPASSWD: ALL > /etc/sudoers.d/ubosdev
      # chmod 600 /etc/sudoers.d/ubosdev
      
    10. No root password:

      # passwd -d root
      
    11. Exit from the arch-chroot shell with ^D.

  11. Remainder of networking setup:

    # rm /mnt/etc/resolv.conf
    # ln -s /run/systemd/resolve/stub-resolv.conf /mnt/etc/resolv.conf
    
  12. Configure UEFI:

    • Loader configuration:

      # echo timeout 4 > /mnt/boot/loader/loader.conf
      # echo default arch >> /mnt/boot/loader/loader.conf
      
    • Boot entry configuration:

      # echo title Arch > /mnt/boot/loader/entries/arch.conf
      # echo linux /vmlinuz-linux >> /mnt/boot/loader/entries/arch.conf
      # echo initrd /initramfs-linux.img >> /mnt/boot/loader/entries/arch.conf
      # echo options root=PARTUUID=$(lsblk -o PARTUUID /dev/sda3 | tail -1 ) rw >> /mnt/boot/loader/entries/arch.conf
      
  13. Power off the virtual machine:

    # systemctl poweroff
    
  14. Remove the ISO file from the VM by clicking on the “[Optical Drive]” in “Storage” and selecting “remove”.

Note

IMPORTANT: Now set Settings / System / “Enable EFI (special OSes only)”, otherwise the newly installed VM won’t boot.

Remaining configuration

  1. Start the “ubosdev” VM again.

  2. At the console, log in as ubosdev. There is no password.

  3. Fix the locale (command won’t run earlier)

    % sudo localectl set-locale LANG=en_US.UTF-8
    
  4. Enable Gnome:

    % sudo systemctl enable gdm
    
  5. Power off the virtual machine:

    % sudo systemctl poweroff
    

Now your virtual machine is in the same state as the pre-configured development VM described in ../setup/.

Create a virtual appliance for distribution

  1. Make sure virtual machine ubosdev_x86_64-vbox-YYYYMMD-I is powered off.

  2. In the menu, select “File / Export Appliance”.

    • In the “Virtual machines” section, select ubosdev_x86_64-vbox-YYYYMMD-I.

    • In “Format settings”, select “Open Virtualization Format 2.0”.

    • The default in “File” may already be correct. The file name needs to contain the processor architecture, the current date and an index: ubosdev_x86_64-vbox-YYYYMMDD-I.ova, e.g. ubosdev-x86_64-vbox-20240405-1.ova. (Don’t add a UBOS channel to the name, as this is Arch.)

    • In “MAC Address Policy”, select “Strip all network adapter MAC addresses”.

    • In “Additionally”, select “Write Manifest file” but not “Include ISO image files” and click “Next”.

  3. In the “Appliance settings”, accept the defaults and click “Finish”.

  4. Writing the file will take some time.

    • If you get a cryptic error message, that may be because VirtualBox on your Mac does not have permissions to write to the default folder ~/Documents. To solve this, either give VirtualBox full disk access (System Settings / Privacy & Security / Full Disk Access: add VirtualBox) or save the file to a directory that it can write to, such as ~/VirtualBox VMs.