Arch Linux ARM on M1 Parallels: Encrypted Root Done Right (A Battle Log & Guide)

So, you’ve decided to tackle Arch Linux ARM on your shiny Apple Mx Mac via Parallels Desktop. Fantastic! You’ve likely made it through the first bits of the installation, but now you want that sweet, sweet full-disk encryption for your root filesystem. You follow the Arch Wiki diligently, but… it just won’t boot. Kernel panics. “Unable to mount root fs on unknown-block(0,0).” Sound familiar?

I’ve been there. After wrestling with this for longer than I’d like to admit, I’ve consolidated the specific steps and common pitfalls that finally got my encrypted Arch ARM VM happily booting. This isn’t a replacement for the official ArchWiki installation guide, but rather a set of crucial hints and corrections, particularly for the ARM and Parallels context.

Let’s dive in.

My Setup Snapshot:

  • Host: Apple MacBook M1
  • Virtualization: Parallels Desktop
  • Guest OS: Arch Linux ARM using the local install from here.
  • Disk Layout:
    • /dev/sda1: 4GB, FAT32, mounted as /boot (EFI partition).
    • /dev/sda2: Rest of disk (approx. 496GB).
      • Encrypted with LUKS: cryptsetup luksFormat /dev/sda2
      • Decrypted device: /dev/mapper/cryptroot
      • LVM Physical Volume (PV): /dev/mapper/cryptroot
      • Volume Group (VG): volgrp
      • Logical Volumes (LVs):
        • volgrp-swap: 4GB, for swap.
        • volgrp-root: Rest of space, formatted as ext4, mounted as /.

The Encryption & LVM Journey (The Standard Part)

The initial steps of setting up LUKS on /dev/sda2, then creating the LVM physical volume, volume group (volgrp), and logical volumes (volgrp-swap, volgrp-root) on top of /dev/mapper/cryptroot are generally well-covered in the ArchWiki. Follow those instructions meticulously. Ensure your filesystems (FAT32 for /boot, ext4 for volgrp-root) are created correctly. Just to note, the next time I do this, I will likely use btrfs for the root filesystem.

The Initramfs: The Crucial Messenger (mkinitcpio.conf)

This is where the kernel gets its early boot instructions, including how to decrypt your disk. Since we’re using a systemd-based init (a common choice for modern Arch installations), our hooks need to reflect that.

Open /etc/mkinitcpio.conf:

sudo vim /etc/mkinitcpio.conf

Find the HOOKS line and ensure it contains the following, in this exact order:

HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole block sd-encrypt lvm2 filesystems fsck)

Key takeaways here:

  • base: Provides essential utilities and a BusyBox recovery shell if systemd fails. Always include it.
  • systemd: This hook is crucial because it sets up the systemd early userspace. If you picked systemd during installation, this is a must.
  • sd-encrypt: This is the systemd-native hook for LUKS decryption. It must be used when systemd is present in your hooks, and it replaces the older encrypt hook.
  • lvm2: Comes after sd-encrypt because LVM volumes reside on the decrypted LUKS container.
  • Order matters! keyboard before sd-vconsole and sd-encrypt ensures your keyboard is ready for the passphrase. block ensures the disk devices are visible before encryption.

After editing, regenerate your initramfs with:

sudo mkinitcpio -P

Always watch for errors here! Ensure it successfully builds /boot/initramfs-linux.img and acknowledges the sd-encrypt and lvm2 hooks.

GRUB: The Bootloader Whisperer (The Sneaky Bits)

GRUB is responsible for loading the kernel and initramfs, and passing kernel parameters. This is where things get particularly tricky on ARM with encryption.

  1. Enable Cryptodisk in GRUB: Open /etc/default/grub:

    sudo vim /etc/default/grub
    

    Ensure this line is present and uncommented:

    GRUB_ENABLE_CRYPTODISK="y"
    

    Why: This tells GRUB to load its own necessary modules to understand and prompt for encrypted disks before the kernel even starts. Without this, GRUB won’t know what to do with your encrypted /dev/sda2.

  2. Kernel Command Line Parameters (GRUB_CMDLINE_LINUX_DEFAULT): This line passes critical information to the kernel.

    GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet rd.luks.name=5cf92567-adf8-46ef-816f-0519aea55b50=cryptroot root=/dev/mapper/volgrp-root"
    

    Let’s break down rd.luks.name:

    • rd.luks.name=UUID=YOUR_LUKS_UUID=YOUR_DECRYPTED_NAME is the most robust way.
    • My specific working configuration used rd.luks.name=5cf92567-adf8-46ef-816f-0519aea55b50=cryptroot. While UUID= is generally preferred for rd.luks.name (e.g., rd.luks.name=UUID=your-uuid-here=cryptroot), the bare UUID also worked for me. The key is that 5cf92567-adf8-46ef-816f-0519aea55b50 must be the exact UUID of your raw encrypted partition (/dev/sda2), and cryptroot must be the name you want the decrypted volume to have.
    • root=/dev/mapper/volgrp-root: This tells the kernel where your actual root filesystem is after decryption and LVM activation. Make sure volgrp-root matches your LVM Logical Volume name exactly.
  3. The Hidden Gem: Ensuring GRUB Loads the Initramfs (/etc/grub.d/10_linux)

    This was the stealthy culprit in my case! Even if mkinitcpio -P builds the initramfs and grub-mkconfig runs, GRUB might not include the initrd command in its generated grub.cfg if it doesn’t find the initramfs file under its expected names.

    Open /etc/grub.d/10_linux (be careful, this is a GRUB generation script!):

    sudo vim /etc/grub.d/10_linux
    

    Look for the for i in loop that iterates through common initramfs filenames. It should look something like this (or similar on ARM):

    for i in "initramfs-linux.img" \
                "initrd.img-${version}" "initrd-${version}.img" \
                "initrd-${alt_version}.img.old" "initrd-${version}.gz" \
                "initrd-${alt_version}.gz.old" "initrd-${version}" \
                "initramfs-${version}.img" "initramfs-${alt_version}.img.old" \
                "initrd.img-${alt_version}" "initrd-${alt_version}.img" \
                "initrd-${alt_version}" "initramfs-${alt_version}.img" \
                "initramfs-genkernel-${version}" \
                "initramfs-genkernel-${alt_version}" \
                "initramfs-genkernel-${GENKERNEL_ARCH}-${version}" \
                "initramfs-genkernel-${GENKERNEL_ARCH}-${alt_version}"; do
        if test -e "${dirname}/${i}" ; then
          initrd_real="${i}"
          break
        fi
    done
    

    My Fix: I had to explicitly ensure "initramfs-linux.img" was present and ideally early in that list. It appears the default 10_linux on my ARM setup wasn’t correctly prioritizing or finding the standard Arch initramfs-linux.img. Adding "initramfs-linux.img" explicitly as the very first option in that list (or ensuring it’s there) solved the issue of GRUB not loading the initramfs.

    After making this change, save the file.

The Final GRUB Generation

Now, with all the pieces in place, regenerate your GRUB configuration:

sudo grub-mkconfig -o /boot/grub/grub.cfg

Crucially, watch the output! You should now see lines confirming:

  • Found Linux image: /boot/vmlinuz-linux
  • Found initrd image: /boot/initramfs-linux.img (This is the big one!)
  • It should also show the kernel command line it’s generating, confirming your cryptdevice and root parameters are correct.

Reboot and Victory!

Exit your chroot environment, unmount all partitions, close the LUKS device, and reboot your VM:

exit
sudo umount /mnt/boot
sudo umount /mnt
sudo cryptsetup close cryptroot
sudo reboot

If all steps were followed, you should now be greeted by a passphrase prompt from GRUB, and upon successful entry, your Arch Linux ARM system should boot seamlessly into your encrypted root!

This process was a true test of patience, highlighting that while Arch Linux is incredibly powerful, solving boot issues on non-standard architectures and encrypted setups requires a deep dive into the specifics of each component. Hopefully, this detailed account saves someone else the late nights!

As always, you can add my RSS feed to your reader of choice and if you made it this far thanks for reading!

Chris