<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Ubuntu | Friedrich Wiemer</title>
    <link>https://asante.dev/tags/ubuntu/</link>
      <atom:link href="https://asante.dev/tags/ubuntu/index.xml" rel="self" type="application/rss+xml" />
    <description>Ubuntu</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>en-us</language><copyright>© 2026 Friedrich Wiemer</copyright><lastBuildDate>Sat, 21 Feb 2026 00:00:00 +0000</lastBuildDate>
    <image>
      <url>https://asante.dev/images/icon_hu_10c2cab00f70a654.png</url>
      <title>Ubuntu</title>
      <link>https://asante.dev/tags/ubuntu/</link>
    </image>
    
    <item>
      <title>Ubuntu 20.04 → 24.04 Dist-Upgrade: LUKS2, GRUB, Secure Boot and systemd-boot</title>
      <link>https://asante.dev/post/ubuntu-20.04-to-24.04-grub-to-sd-boot/</link>
      <pubDate>Sat, 21 Feb 2026 00:00:00 +0000</pubDate>
      <guid>https://asante.dev/post/ubuntu-20.04-to-24.04-grub-to-sd-boot/</guid>
      <description>&lt;p&gt;This page documents debugging a failed Ubuntu release upgrade that left the system
unbootable. The root causes were an interplay between LUKS2 encryption, GRUB 2.12,
Secure Boot and missing dotfiles — each amplifying the others into a long chain of
cascading problems.&lt;/p&gt;
&lt;h2 id=&#34;starting-point&#34;&gt;Starting Point&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Xubuntu 20.04, LUKS2-encrypted root partition&lt;/li&gt;
&lt;li&gt;Dual-boot with Windows on the same NVMe SSD&lt;/li&gt;
&lt;li&gt;A second encrypted partition (Veracrypt/exFAT) as shared storage,
mounted at &lt;code&gt;/home/user/data&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Personal dotfiles (xmonad, zsh, vim, etc.) stored as symlinks on this
Veracrypt partition&lt;/li&gt;
&lt;li&gt;Neo2 keyboard layout, xmonad as window manager, Alacritty as terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;phase-1-failed-dist-upgrade&#34;&gt;Phase 1: Failed Dist-Upgrade&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;do-release-upgrade&lt;/code&gt; from 20.04 → 22.04 was started. The upgrade stalled on the
package &lt;strong&gt;ConTeXt MarkIV&lt;/strong&gt; (part of TeXLive) — a very large package whose
post-install script runs for a long time. It was manually interrupted.&lt;/p&gt;
&lt;p&gt;The system no longer booted afterwards. GRUB dropped directly into its interactive
command line (&lt;code&gt;grub&amp;gt;&lt;/code&gt;) without showing a boot menu.&lt;/p&gt;
&lt;h2 id=&#34;phase-2-chroot-repair-via-live-usb&#34;&gt;Phase 2: Chroot Repair via Live-USB&lt;/h2&gt;
&lt;p&gt;The system was booted from a Live-USB. The encrypted partition was opened manually
and a chroot environment was set up:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;cryptsetup luksOpen /dev/sdXY cryptroot
mount /dev/mapper/cryptroot /mnt
mount /dev/sdXZ /mnt/boot/efi          # EFI partition
mount --bind /dev /mnt/dev
mount --bind /proc /mnt/proc
mount --bind /sys /mnt/sys
mount --bind /run /mnt/run
mount --bind /sys/firmware/efi/efivars /mnt/sys/firmware/efi/efivars
chroot /mnt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the chroot:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dpkg --configure -a&lt;/code&gt; and &lt;code&gt;apt --fix-broken install&lt;/code&gt; repaired broken packages&lt;/li&gt;
&lt;li&gt;The kernel was missing entirely (only initrd present, no vmlinuz):
&lt;code&gt;apt install --reinstall linux-image-generic&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The upgrade was completed with &lt;code&gt;do-release-upgrade&lt;/code&gt; to 22.04 and then to 24.04&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;phase-3-grub--luks2--secure-boot--the-core-problem&#34;&gt;Phase 3: GRUB + LUKS2 + Secure Boot — The Core Problem&lt;/h2&gt;
&lt;h3 id=&#34;symptom&#34;&gt;Symptom&lt;/h3&gt;
&lt;p&gt;After the upgrade GRUB still showed no menu entries. Debugging in the GRUB
command line:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grub&amp;gt; ls (hd1,gpt7)
# → &amp;quot;No known filesystem detected&amp;quot;

grub&amp;gt; insmod luks2
# → &amp;quot;file &#39;/boot/grub/x86_64-efi/luks2.mod&#39; not found&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;root-cause&#34;&gt;Root Cause&lt;/h3&gt;
&lt;p&gt;The real problem is a fundamental incompatibility:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Canonical&amp;rsquo;s pre-signed &lt;code&gt;grubx64.efi&lt;/code&gt; deliberately does not include the
&lt;code&gt;luks2&lt;/code&gt; module.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;GRUB 2.12 is technically capable of decrypting LUKS2 (provided PBKDF2 is used
as the key derivation function rather than Argon2), but the Secure-Boot-signed
EFI binary shipped by Ubuntu intentionally omits this module — it is still
considered not fully stable.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;grub-install --modules=&amp;quot;... luks2 ...&amp;quot;&lt;/code&gt; therefore has no effect: with Secure Boot
active, &lt;code&gt;grub-install&lt;/code&gt; always uses the pre-signed image and ignores the
&lt;code&gt;--modules&lt;/code&gt; flag.&lt;/p&gt;
&lt;p&gt;A custom image built with &lt;code&gt;grub-mkimage&lt;/code&gt; that includes &lt;code&gt;luks2&lt;/code&gt; support is
immediately rejected by Secure Boot:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Verification failed: (0x1A) Security Violation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Additionally, &lt;code&gt;grub-install&lt;/code&gt; failed silently inside the chroot because EFI
variables cannot be written from within a chroot environment
(&lt;code&gt;grub-efi/install_devices_failed: true&lt;/code&gt; in &lt;code&gt;debconf-show grub-efi&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id=&#34;options&#34;&gt;Options&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Option&lt;/th&gt;
					&lt;th&gt;Effort&lt;/th&gt;
					&lt;th&gt;Downside&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Disable Secure Boot&lt;/td&gt;
					&lt;td&gt;low&lt;/td&gt;
					&lt;td&gt;Secure Boot inactive&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Convert LUKS2 → LUKS1&lt;/td&gt;
					&lt;td&gt;low&lt;/td&gt;
					&lt;td&gt;No backup header&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Own MOK key + self-signed GRUB&lt;/td&gt;
					&lt;td&gt;high&lt;/td&gt;
					&lt;td&gt;Must be repeated on every GRUB update&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Switch to systemd-boot&lt;/td&gt;
					&lt;td&gt;medium&lt;/td&gt;
					&lt;td&gt;One-time setup effort&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Converting to LUKS1 was ruled out because LUKS2&amp;rsquo;s backup header had already
proven invaluable once before. systemd-boot was chosen as the solution.&lt;/p&gt;
&lt;h2 id=&#34;phase-4-switching-to-systemd-boot&#34;&gt;Phase 4: Switching to systemd-boot&lt;/h2&gt;
&lt;h3 id=&#34;why-systemd-boot&#34;&gt;Why systemd-boot?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Secure Boot is natively supported (the binary is signed by the distribution)&lt;/li&gt;
&lt;li&gt;LUKS is not opened by systemd-boot itself — that is handled by the initramfs
during kernel startup, which eliminates the LUKS2 problem entirely&lt;/li&gt;
&lt;li&gt;Dual boot with Windows works out of the box: systemd-boot automatically
detects the Windows EFI entry&lt;/li&gt;
&lt;li&gt;No shim intermediary required (unlike GRUB)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;bootctl install --esp-path=/boot/efi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The kernel and initrd are automatically copied to the EFI partition by
&lt;code&gt;kernel-install&lt;/code&gt;. A new boot entry is created on every kernel update.&lt;/p&gt;
&lt;h3 id=&#34;luks-parameters&#34;&gt;LUKS Parameters&lt;/h3&gt;
&lt;p&gt;The automatically generated boot entry contained no LUKS parameters. These are
stored permanently in &lt;code&gt;/etc/kernel/cmdline&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rd.luks.uuid=&amp;lt;UUID-of-LUKS-partition&amp;gt; rd.luks.name=&amp;lt;UUID&amp;gt;=cryptroot root=/dev/mapper/cryptroot rw quiet splash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;kernel-install add &amp;lt;version&amp;gt; /boot/vmlinuz-&amp;lt;version&amp;gt;&lt;/code&gt; regenerates the entry with
these parameters. Future kernel updates will automatically use the correct
parameters.&lt;/p&gt;
&lt;h3 id=&#34;secure-boot-problem&#34;&gt;Secure Boot Problem&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;systemd-bootx64.efi&lt;/code&gt; was not signed on this system — Ubuntu does not ship a
&lt;code&gt;.efi.signed&lt;/code&gt; package for systemd-boot. &lt;strong&gt;Temporary solution: Secure Boot
disabled in BIOS.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For a clean solution with Secure Boot re-enabled:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# Generate own signing key
openssl req -newkey rsa:4096 -nodes -keyout /root/mok.key \
  -new -x509 -sha256 -days 3650 \
  -subj &amp;quot;/CN=My Boot Signing Key/&amp;quot; -out /root/mok.crt

# Sign systemd-boot
sbsign --key /root/mok.key --cert /root/mok.crt \
  --output /boot/efi/EFI/systemd/systemd-bootx64.efi \
  /boot/efi/EFI/systemd/systemd-bootx64.efi

# Register key with Secure Boot
mokutil --import /root/mok.crt
# → Confirm once in the MOK manager on next boot
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;efi-boot-order&#34;&gt;EFI Boot Order&lt;/h3&gt;
&lt;p&gt;The old GRUB entry was removed and systemd-boot set as the first entry:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;efibootmgr --delete-bootnum --bootnum XXXX   # remove old Ubuntu/GRUB entry
efibootmgr --bootorder 0001                  # Linux Boot Manager first
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;phase-5-veracrypt-partition--the-actual-mount-error&#34;&gt;Phase 5: Veracrypt Partition — The Actual Mount Error&lt;/h2&gt;
&lt;h3 id=&#34;symptom-1&#34;&gt;Symptom&lt;/h3&gt;
&lt;p&gt;The Veracrypt partition was not mounted at boot. The journal showed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mount: wrong fs type, bad option, bad superblock on /dev/mapper/data,
missing codepage or program, or other error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looked like a serious problem — but it wasn&amp;rsquo;t.&lt;/p&gt;
&lt;h3 id=&#34;diagnosis-with--v&#34;&gt;Diagnosis with &lt;code&gt;-v&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;cryptsetup open /dev/sdXY data \
  --type tcrypt --veracrypt \
  --key-file /home/user/.data.key -v
# → Error: device already exists
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;device already exists&lt;/code&gt; means: cryptsetup had already successfully opened the
partition. The only problem was the subsequent mount step.&lt;/p&gt;
&lt;h3 id=&#34;cause-invalid-fstab-options-for-exfat&#34;&gt;Cause: Invalid fstab Options for exFAT&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;/etc/fstab&lt;/code&gt; contained for this partition:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/dev/mapper/data  /home/user/data  auto  permissions,users,nosuid,nodev,nofail,...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;permissions&lt;/code&gt; is not a valid mount option for exFAT filesystems and causes an
immediate mount failure. The corrected line:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/dev/mapper/data  /home/user/data  exfat  nofail,fmask=133,dmask=022,uid=1000,gid=1000  0  0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;phase-6-keyboard-layout-chaos-due-to-missing-dotfiles&#34;&gt;Phase 6: Keyboard Layout Chaos Due to Missing Dotfiles&lt;/h2&gt;
&lt;h3 id=&#34;what-happened&#34;&gt;What Happened&lt;/h3&gt;
&lt;p&gt;Because the Veracrypt partition was not mounted, all symlink targets were missing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.xmonad/&lt;/code&gt; → xmonad did not start; XFCE was loaded as a fallback&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.zshrc&lt;/code&gt; → attempted to source files from the unmounted partition&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.vimrc&lt;/code&gt; → dangling symlink&lt;/li&gt;
&lt;li&gt;xkb/xmodmap configuration missing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;XFCE initialized the keyboard layout without the user-specific xkb configuration.
The result was a &lt;code&gt;BadKey (0xcf)&lt;/code&gt; registered as the &lt;code&gt;mod2&lt;/code&gt; modifier in X11 —
visible with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;xmodmap -pm
# mod2  BadKey (0xcf)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This invalid modifier entry activated Layer 4 special characters instead of
normal input. In Neo2, Layer 4 maps the number keys to symbols like &lt;code&gt;º&lt;/code&gt;, &lt;code&gt;£&lt;/code&gt;,
&lt;code&gt;¤&lt;/code&gt; — which explains why the keyboard appeared completely broken.&lt;/p&gt;
&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;
&lt;p&gt;After correctly mounting the Veracrypt partition, xmonad started normally with
the existing dotfiles. The keyboard layout worked immediately without any further
intervention, as the xmodmap configuration was loaded correctly.&lt;/p&gt;
&lt;h2 id=&#34;summary-lessons-learned&#34;&gt;Summary: Lessons Learned&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;LUKS2 + GRUB + Secure Boot&lt;/strong&gt; is currently not possible out of the box on Ubuntu.
The combination requires either a custom MOK key with a self-built GRUB image,
converting to LUKS1, or switching to a different bootloader. systemd-boot is the
most elegant solution here since it delegates LUKS to the initramfs and supports
Secure Boot natively.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dotfiles on encrypted partitions&lt;/strong&gt; are a double-edged sword: they are secure,
but if the partition is unavailable at boot, the entire session initialisation
breaks silently and with cryptic downstream errors. A minimal fallback
configuration directly in the home directory is advisable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;cryptsetup -v&lt;/code&gt;&lt;/strong&gt; is essential when debugging — without verbose output,
&amp;ldquo;device already exists&amp;rdquo; looks identical to a real failure.&lt;/p&gt;
&lt;h2 id=&#34;open-items&#34;&gt;Open Items&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Re-enable Secure Boot via own MOK key for systemd-boot&lt;/li&gt;
&lt;li&gt;Clean up stale &lt;code&gt;/boot&lt;/code&gt; fstab entry&lt;/li&gt;
&lt;li&gt;Python symlink: &lt;code&gt;sudo ln -s /usr/bin/python3 /usr/bin/python&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Investigate audio errors (alsactl) in the journal&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
