GRUB and LUKS Screwed My System¶
Introduction¶
After helping my 76‑year‑old father with his first Linux installation (a bold move for anyone at that age), I wanted to disable the graphical boot splash on my Linux Mint 22.04 system. I edited the GRUB configuration, adding the nomodeset kernel parameter and removing the quiet splash options.
NOTE: The
nomodesetflag tells the kernel not to load video drivers during early boot, which can be useful for troubleshooting display problems.quietsuppresses most boot messages andsplashshows the graphical boot screen.
What Went Wrong?¶
After applying the changes, the system became unbootable. Rebooting only gave me the UEFI boot manager; the Linux kernel never started. I rescued the machine with a live USB created using Ventoy and mounted the encrypted LUKS partition from the command line.
Steps I Took¶
Boot the live environment and executed the following python script Perplexity and i created/drafted/troubleshooted together:
#!/usr/bin/env python3
import subprocess
import os
import sys
import re
from pathlib import Path
def run_cmd(cmd, check=True, capture=True, cwd=None):
"""Run a shell command and return output or raise error."""
try:
result = subprocess.run(cmd, shell=True, check=check,
capture_output=capture, text=True, cwd=cwd)
return result.stdout.strip() if capture else None
except subprocess.CalledProcessError as e:
print(f"ERROR: Command failed: {cmd}\n{e.stderr}")
return None
def get_disk_layout():
"""Auto-discover disk layout from lsblk -f."""
output = run_cmd("lsblk -f -o NAME,FSTYPE,UUID,MOUNTPOINTS")
if not output:
return None
layout = {}
lines = output.split('\n')[1:] # Skip header
for line in lines:
parts = line.split(maxsplit=3)
if len(parts) >= 3:
name, fstype, uuid = parts[0], parts[1], parts[2]
if 'crypto_LUKS' in fstype:
layout['luks'] = name
elif 'vfat' in fstype or 'FAT32' in fstype:
layout['efi'] = name
elif name.endswith('root'):
layout['root_lv'] = name
# Find boot partition (ext4 not mounted)
nvme_parts = [line for line in lines if 'nvme' in line and 'ext4' in line]
layout['boot'] = nvme_parts[0].split()[0] if nvme_parts else None
return layout
def confirm_step(message):
"""Interactive confirmation."""
while True:
response = input(f"{message} [y/N]: ").lower().strip()
if response in ['y', 'yes']:
return True
elif response in ['n', 'no']:
return False
print("Please answer y or n")
def unlock_luks(luks_dev):
"""Unlock LUKS container."""
print(f"🔓 Unlocking LUKS: {luks_dev}")
if confirm_step("Enter your LUKS passphrase now?"):
result = run_cmd(f"cryptsetup open {luks_dev} cryptlvm")
if result:
print("✅ LUKS unlocked!")
return True
return False
def main():
print("🛠️ Linux Mint GRUB EFI Recovery Script")
print("=" * 50)
# Step 1: Auto-discover layout
print("\n📊 Discovering disk layout...")
layout = get_disk_layout()
if not layout:
print("❌ Could not auto-detect layout. Run 'lsblk -f' manually.")
sys.exit(1)
print("Detected:")
print(f" EFI: {layout.get('efi', 'Not found')}")
print(f" LUKS: {layout.get('luks', 'Not found')}")
print(f" Root LV: {layout.get('root_lv', 'Not found')}")
print(f" Boot: {layout.get('boot', 'Not found')}")
if not confirm_step("\nDoes this look correct?"):
print("Please correct manually and rerun.")
sys.exit(1)
# Step 2: Unlock LUKS + LVM
luks_dev = layout['luks']
if not unlock_luks(luks_dev):
sys.exit(1)
if run_cmd("vgchange -ay"):
print("✅ LVM activated")
else:
print("⚠️ LVM activation failed, continuing...")
# Step 3: Mount filesystems
print("\n📁 Mounting filesystems...")
mounts = []
root_lv = layout['root_lv']
efi_part = layout['efi']
boot_part = layout['boot']
if run_cmd(f"mount /{root_lv} /mnt"):
mounts.append('/mnt')
print("✅ Root mounted")
if boot_part and run_cmd(f"mount /{boot_part} /mnt/boot"):
mounts.append('/mnt/boot')
print("✅ /boot mounted")
if run_cmd(f"mkdir -p /mnt/boot/efi") and run_cmd(f"mount /{efi_part} /mnt/boot/efi"):
mounts.append('/mnt/boot/efi')
print("✅ EFI mounted")
# Step 4: Bind mounts
print("\n🔗 Binding system directories...")
for bind in ['/dev', '/dev/pts', '/proc', '/sys', '/run']:
if run_cmd(f"mount --bind {bind} /mnt{bind}"):
mounts.append(f'/mnt{bind}')
print(f"✅ {bind} bound")
# Step 5: Chroot and fix GRUB
print("\n🐚 Entering chroot...")
print("Inside chroot, we'll:")
print(" 1. Check EFI contents")
print(" 2. Show current /etc/default/grub")
print(" 3. Fix GRUB config")
print(" 4. Reinstall GRUB + EFI entry")
if confirm_step("Continue to chroot and fix GRUB?"):
# Show EFI contents first
print("\n📂 Checking EFI contents...")
efi_list = run_cmd("ls -la /mnt/boot/efi/EFI/", capture=False)
print(efi_list)
# Show current GRUB config
print("\n📄 Current /etc/default/grub:")
grub_config = run_cmd("cat /mnt/etc/default/grub")
print(grub_config)
print("\n🔧 Fixing GRUB config...")
# Create safe GRUB config
safe_grub = '''GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="Linux Mint"
GRUB_CMDLINE_LINUX_DEFAULT=""
GRUB_CMDLINE_LINUX="nomodeset"
GRUB_DISABLE_OS_PROBER=false
'''
with open('/mnt/etc/default/grub', 'w') as f:
f.write(safe_grub)
print("✅ Safe GRUB config written")
# Chroot commands
chroot_cmds = [
"mount --make-rslave /mnt",
"chroot /mnt /bin/bash -c 'update-grub'",
"chroot /mnt /bin/bash -c 'efibootmgr -v'",
"chroot /mnt /bin/bash -c 'grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=linuxmint --recheck --no-nvram'",
"chroot /mnt /bin/bash -c 'efibootmgr --create --disk /dev/nvme0n1 --part 1 --label \"Linux Mint\" --loader \"\\\\EFI\\\\linuxmint\\\\grubx64.efi\"'",
"chroot /mnt /bin/bash -c 'update-grub'"
]
print("\n🚀 Running GRUB repair...")
for cmd in chroot_cmds:
print(f"Running: {cmd}")
if run_cmd(cmd):
print("✅ OK")
else:
print("⚠️ Failed, continuing...")
print("\n✅ GRUB repair complete!")
# Step 6: Cleanup
print("\n🧹 Unmounting...")
for mount in reversed(mounts):
run_cmd(f"umount {mount}", check=False)
print("🔒 Closing LUKS...")
run_cmd("cryptsetup close cryptlvm", check=False)
run_cmd("vgchange -an vgmint", check=False)
print("\n🎉 Recovery complete!")
print("Reboot now (remove USB): sudo reboot")
if confirm_step("Reboot now?"):
run_cmd("reboot")
if __name__ == "__main__":
# Check if running as root
if os.geteuid() != 0:
print("❌ Run as root: sudo python3 grub_recovery.py")
sys.exit(1)
# Safety check
if confirm_step("This will modify your disk. Continue?"):
main()
else:
print("Aborted safely.")
The system booted normally again.
The After‑effects¶
Even though the system started, video performance was terrible. Cinnamon on X was falling back to software rendering because the amdgpu kernel module never loaded – the nomodeset flag had been left in the configuration.
Symptoms¶
- Low frame rates, high CPU usage.
- Incorrect monitor detection; scaling options unavailable.
- Flickering and occasional freezes.
Fixing the Grub Configuration Safely¶
I wanted a way to toggle nomodeset without permanently breaking the system. With the help of Perplexity and gpt‑me, I wrote a small script that:
1. Backs up the current /etc/default/grub.
2. Adds a new menu entry called “Linux Mint (nomodeset)” that includes the nomodeset flag.
3. Leaves the default entry untouched.
#!/bin/bash
set -e
echo "=== Removing nomodeset from default GRUB ==="
sudo sed -i 's/^GRUB_CMDLINE_LINUX="nomodeset"$/GRUB_CMDLINE_LINUX=""/' /etc/default/grub
echo "=== Creating nomodeset fallback entry ==="
sudo tee /etc/grub.d/40_custom > /dev/null << 'EOF'
#!/bin/sh
exec tail -n +3 $0
menuentry 'Linux Mint 22.2 (nomodeset fallback)' --class linuxmint --class gnu-linux --class gnu --class os {
load_video
insmod gzio
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root a9c4e8d5-aa77-4b2d-9b7e-8c3f1e2d4b6a
linux /vmlinuz-6.14.0-37-generic root=/dev/mapper/vgmint-root ro nomodeset
initrd /initrd.img-6.14.0-37-generic
}
EOF
sudo chmod +x /etc/grub.d/40_custom
echo "=== Updating GRUB ==="
sudo update-grub
echo "=== Done! Reboot to test. Fallback entry available in GRUB menu. ==="
Running the script inside the chroot (or on the installed system) gave me a safe way to test nomodeset when needed, while keeping the normal graphical boot as the default.
Lessons Learned¶
- Always keep a backup of
/etc/default/grubbefore editing. - Use a custom menu entry rather than overwriting the default line.
- When dealing with encrypted installations, remember to unlock the LUKS volume and bind‑mount the necessary pseudo‑filesystems before chrooting.
- Always have a Ventoy enabled boot-disk ready with a recent LTS version of the Ubuntu Mint ISO ready.
- Tools like Ventoy, Perplexity, and gpt‑me can dramatically speed up troubleshooting, but verify the generated commands before executing them.
Original article written on 2025‑12‑30.