How to create a UBOS development VM for UTM on Apple Silicon computers

/docs/linux/developer/create-developer-vm/apple-silicon-utm/

Install UTM

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

Obtain an Arch Linux ARM image

  1. Find the Arch Linux ARM image pre-built by UTM and select “Open in UTM”.

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

  1. In UTM, add a virtual disk that becomes the development VM:

    • Select the downloaded “ArchLinux” virtual machine and open the settings (click on the three sliders icon all the way to the right in the toolbar). Adjust the following settings:

      • In the “Information” section:

        • Change the name to “ubosdev_aarch64-utm-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.)
      • In the “Drives” section of the sidebar, click “New…”:

        • Select “interface: VirtIO”

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

        • Click “Create”. This will become the root disk of the UBOS development VM.

        • Click “Save”.

  2. Now start the virtual machine by selecting it in the sidebar, and clicking the run icon (">”). Accept the defaults in the boot loader (or just wait) and wait until the boot sequence ends and the login prompt appears.

Install Arch on the empty disk and configure it

  1. When the login prompt appears on the console, log in with “root” / “root”.

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

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

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

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

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

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

    # mkfs.vfat  /dev/vdb2
    # mkfs.btrfs /dev/vdb3
    
  8. Mount the partitions so we can install:

    # mount /dev/vdb3 /mnt
    # mkdir /mnt/boot
    # mount /dev/vdb2 /mnt/boot
    
  9. Perform the actual install:

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

    # genfstab -U -p /mnt >> /mnt/etc/fstab
    
  11. 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.xz
      # pacman -U ubos-keyring-0.9-2-any.pkg.tar.xz
      # rm ubos-keyring-0.9-2-any.pkg.tar.xz
      
    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: (FIXME: CHECK whether we have archlinuxarm-keyring here already)

      # pacman -Sy
      # pacman -S linux-aarch64 mkinitcpio amd-ucode sudo vim btrfs-progs spice-vdagent qemu-guest-agent \
        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. Create a ramdisk:

      # mkinitcpio -p linux-aarch64
      
    5. Configure the boot loader:

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

      # perl -pi -e 's!#en_US.UTF-8 UTF-8!en_US.UTF-8 UTF-8!' /etc/locale.gen
      # locale-gen
      
    7. 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 'IPv4Forwarding=1' >> /etc/systemd/network/wired.network
      # echo 'IPv6Forwarding=1' >> /etc/systemd/network/wired.network
      
      # systemctl enable systemd-networkd systemd-resolved systemd-timesyncd
      
    8. 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
      
    9. No root password:

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

  12. Remainder of networking setup:

    # rm /mnt/etc/resolv.conf
    # ln -s /run/systemd/resolve/stub-resolv.conf /mnt/etc/resolv.conf
    
  13. 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 /Image >> /mnt/boot/loader/entries/arch.conf
      # echo initrd /amd-ucode.img >> /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/vdb3 | tail -1 ) rootfstype=btrfs rw cgroup_disable=memory add_efi_memmap >> /mnt/boot/loader/entries/arch.conf
      
  14. Power off the virtual machine:

    # systemctl poweroff
    

Use the newly installed disk for the actual development virtual machine

  1. UTM has no direct functionality to detach the newly created root disk for ubosdev from the boostrap instance (issue filed), so we have to do it manually.

    • In UTM, select the Arch Linux virtual machine. In the popup, select “Show in Finder”.

    • Quit UTM.

    • In the Finder, keep “ubosdev_aarch64-utm-YYYYMMDD-I.utm” selected and in the popup, select “Show package contents”.

    • In the Finder, open “config.plist” with a text editor.

    • In the XML file, find the entry <key>Drive</key> and the three-element array that follows right after. They are likely towards the beginning of the file.

    • This array contains three dicts, each representing a virtual hard drive. In the third dict, note the value of the string right after <key>ImageName</key>: this is the name of the file that contains the new boot disk. This name is long UUID-style name.

    • Leave the text editor open. In the Finder, open subdirectory “Data”, and find the file whose name you identified in the previous step. Move this file out of the current directory into a temporary location; do not delete the file.

    • In the text editor, delete this third dict in its entirety.

    • Save “config.plist” and close the text editor.

  2. UTM also doesn’t have much built-in functionality to clone the bootstrap VM, so we use our existing, working VM as the template.

    • Start UTM again.

    • Run the Arch Linux VM, and make sure it boots without problem. Log in back as “root” / “root” and shut it back down with systemctl poweroff.

    • In UTM, select the Arch Linux VM, and in the popup, select “Clone” and confirm.

    • The cloned VM is now selected in UTM. In the popup, select “Edit”.

    • Change the name to a name that also contains the processor architecture, the current date and an index: ubosdev_aarch64-utm-YYYYMMDD-I, e.g. ubosdev_aarch64-utm-20240405-1. (Don’t add a UBOS channel to the name, as this is Arch.)

    • In the “System” tab, change the memory to 8192MB and save.

    • Select VM “ubosdev_aarch64-utm-YYYYMMDD-I”, and in the popup, select “Show in Finder”.

    • Quit UTM.

    • In the Finder, keep “ubosdev_aarch64-utm-YYYYMMDD-I.utm” selected, and in the popup, select “Show Package Contents”.

    • Open “config.plist” with a text editor.

    • In the XML file, find the entry <key>Drive</key> and the two-element array that follows right after. They are likely towards the beginning of the file.

    • Similarly to the above, in the second of those dicts, note the value of the string right after <key>ImageName</key>.

    • In the Finder, select the “Data” directory. It should contain a file with the just noted name. Delete that file.

    • In the Finder, move into that same “Data” directory the file you previously put into a temporary location.

    • In the text editor, in the first array, in the second dict element, replace strings for keys Identifier and ImageName with the name of the file you previously put into a temporary location and now moved into “Data”. Note that the Identifier does not want the extension, while ImageName does.

    • Save the plist file and close the text editor.

Remaining configuration

  1. Start UTM again.

  2. Start “ubosdev_aarch64-utm-YYYYMMDD-I” VM and wait until the login prompt appears.

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

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

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

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

    % sudo systemctl poweroff
    

Add virtual graphics for the VM

  1. In UTM, select the “ubosdev_aarch64-utm-YYYYMMDD-I” VM, and in the popup, “Edit”.

  2. Under “Devices”, click “New…” and “Display”.

  3. Click “Save”.

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

Create a file for distribution

  1. In UTM, select the “ubosdev_aarch64-utm-YYYYMMDD-I” VM and in the popup, “Share…”

  2. Save the file with a name that also contains the processor architecture, the current date and an index: ubosdev_aarch64-utm-YYYYMMDD-I.utm, e.g. ubosdev_aarch64-utm-20240405-1.utm. (Don’t add a UBOS channel to the name, as this is Arch.)

  3. The .utm file is actually a directory. Put it into a zip file:

    % zip -r ubosdev_yellow_aarch64-utm-YYYYMMDD-I.utm.zip ubosdev_yellow_aarch64-utm-YYYYMMDD-I.utm