Prompt Your Way to Linux: A 4-Part Series
Part 1: Picking Your Distro → Part 2: Storage and Encryption → Part 3: Manual Install → Part 4: Services and GPU
Picture this: you've just spent two hours building a carefully designed encrypted LVM layout. Six logical volumes, three encrypted disks, everything exactly where it needs to be. Then the Kali installer tries to flatten the whole thing into a single ext4 partition.
The graphical installer already crashed from the hybrid GPU. Now the text installer is actively trying to undo your work. You could fight it for another three hours. Or you could describe the problem to AI and discover something you probably haven't considered: skip the installer entirely and build the OS by hand with debootstrap. That pivot will save the entire build.
The Installer Was Supposed to Just Work
After spending hours in Part 2 crafting an encrypted LVM storage layout with separate volumes for root, swap, docker, ollama, postgres, and home, the next step seems almost boring. Launch the Kali installer, choose manual partitioning, assign mount points, click next a few times, done. Twenty minutes, tops.
That expectation is completely wrong.
The graphical installer will crash immediately on hybrid GPU hardware. Intel integrated graphics alongside an NVIDIA discrete GPU? The GUI installer can't handle the hybrid framebuffer. Black screens. X server failures. Total dead end. Not a single menu renders before the whole thing locks up.
Fine. The text installer it is.
The text-based installer avoids the GPU crash, and for a moment things look promising. It boots, detects the NVMe drive, offers a manual partitioning option. But then it introduces a problem far worse than a crashed GUI.
The Moment You Need to Stop
Here's what happens inside the text installer's manual partitioning screen: it detects nvme0n1p3_crypt (the opened LUKS container) and keeps trying to format it as plain ext4.
That's not what it is. That mapped device is supposed to be an LVM physical volume sitting inside the encrypted container. On top of that PV sits a volume group, and on top of that VG sit six logical volumes: root, swap, docker, ollama, postgres, and home. If the installer writes an ext4 filesystem directly onto nvme0n1p3_crypt, it will obliterate the entire multi-volume design in about three seconds.
Describe this to AI. The response will be immediate and specific:
"Stop. Don't proceed. The installer is misinterpreting your storage stack. It's treating your LUKS container as a raw partition instead of recognizing the LVM layer inside it. If you let it format, you'll lose every logical volume."
The strange part? Drop to a shell from the installer and run lvs. Every logical volume shows up perfectly. The live environment understands the storage layout. The installer's partitioning tool does not. It sometimes shows the LUKS container, sometimes shows the volume group, but rarely displays the actual logical volumes where mount points need to go.
The pattern is unmistakable once you know what to look for: the live shell sees everything correctly; the installer sees wrong things. That makes the installer untrustworthy for this build.
This is where the whole approach pivots. Stop asking "how do we make the installer work" and start asking "how do we install Kali without the installer?"
The Manual Install Path: debootstrap
The answer is clean: mount the target filesystems yourself, use debootstrap to pull down a base system, chroot into it, and build the OS by hand.
This isn't a downgrade or a desperate workaround. For a complex, multi-volume encrypted build like this one, it's actually the professional approach. Here's why it beats wrestling with the installer:
- Preserves the storage design exactly as built. No installer reinterpreting your layout.
- Avoids GUI instability on hybrid GPU hardware entirely.
- Gives complete control over package selection and configuration.
- More predictable than retrying the installer and hoping it reads LVM correctly this time.
Walking Through It, Step by Step
This is the part where the process gets genuinely satisfying. Each step is a prompt, a response, and then execution. Here's the full sequence.
Step 1: Mount the target filesystem tree
Start with the root logical volume, then layer everything else on top. The order matters:
1mount /dev/mapper/vg0-root /mnt
2mkdir -p /mnt/boot /mnt/boot/efi /mnt/home
3mkdir -p /mnt/var/lib/docker /mnt/var/lib/ollama /mnt/var/lib/postgresql
4mount /dev/nvme0n1p2 /mnt/boot
5mount /dev/nvme0n1p1 /mnt/boot/efi
6mount /dev/mapper/vg0-home /mnt/home
7mount /dev/mapper/vg0-docker /mnt/var/lib/docker
8mount /dev/mapper/vg0-ollama /mnt/var/lib/ollama
9mount /dev/mapper/vg0-postgres /mnt/var/lib/postgresqlWhy mount everything first? When debootstrap writes files, they land on the correct target volumes from the start. If you only mount root and bootstrap, then mount /home later, you'll have bootstrap artifacts sitting on the root volume where /home should be.
Step 2: Bootstrap the base system
debootstrap --arch=amd64 kali-rolling /mnt http://http.kali.org/kaliBefore entering the chroot, bind-mount the host's pseudo-filesystems so the chroot environment can interact with hardware and the network:
1mount --bind /dev /mnt/dev
2mount --bind /dev/pts /mnt/dev/pts
3mount --bind /proc /mnt/proc
4mount --bind /sys /mnt/sys
5mount --bind /sys/firmware/efi/efivars /mnt/sys/firmware/efi/efivars
6cp /etc/resolv.conf /mnt/etc/resolv.conf
7chroot /mnt /bin/bashEach of those bind mounts serves a specific purpose: /dev and /dev/pts let package scripts access devices and terminals. /proc and /sys are needed for initramfs generation and kernel module detection. The EFI vars mount is critical for GRUB EFI installation. And copying resolv.conf gives the chroot DNS resolution so apt can actually reach repositories.
Step 3: Configure everything inside the chroot
Here's the full configuration checklist, and each one matters:
- Apt sources pointing to
kali-rolling main contrib non-free non-free-firmware - Core packages:
linux-image-amd64,systemd,cryptsetup-initramfs,lvm2,grub-efi-amd64 - Desktop environment:
xorg,xfce4,lightdm,kali-desktop-xfce - System configuration: hostname, locale (
en_US.UTF-8), timezone - User creation:
adduser, adding tosudogroup - Crypttab and fstab: referencing the correct UUIDs for each layer
- Initramfs rebuild:
update-initramfs -u -k allto include LUKS and LVM hooks - GRUB install:
grub-install --target=x86_64-efi --efi-directory=/boot/efifollowed byupdate-grub
The whole process takes about 45 minutes. Not because it's difficult, but because each step needs deliberate attention. Feed each step to AI, get the commands, understand the reasoning, and watch for pitfalls before they happen.
First Boot and the Sudo Surprise
After debootstrap, chroot configuration, initramfs generation, and GRUB installation, it's the moment of truth. Unmount everything in reverse order, pull out the USB, reboot.
The first boot works. The LUKS passphrase prompt appears exactly as expected. Enter the passphrase, GRUB loads, Kali boots, LightDM displays the login screen, XFCE renders. Everything that failed with the graphical installer is now running perfectly from a hand-built system.
But then comes a small, annoying surprise. Your user account can't run sudo. Every command returns:
user is not in the sudoers file. This incident will be reported.You definitely added the user to the sudo group during the chroot setup. You can verify it with groups and see sudo listed. So what's going on?
Here's what's actually happening: the current login session hasn't picked up the group change. Group membership is evaluated at login time. Since the user was created and added to sudo inside the chroot but never actually logged in through a proper session manager, the running session doesn't reflect the membership. The fix? Log out and log back in. That's it.
This is one of those classic Linux gotchas that trips up beginners and experienced users alike. You know you added the group. You can prove you added the group. But the system doesn't care what's in /etc/group until the next login evaluates it. Without AI pointing this out immediately, you could easily burn an hour going down a rabbit hole of PAM configurations and sudoers file edits for nothing.
The Emergency Mode Incident
Buckle up. This is the single most valuable debugging lesson from the entire four-part build, and it reads like a whodunit where every clue points at the wrong suspect.
After the successful first boot and some additional configuration work (including installing NVIDIA proprietary drivers for the discrete GPU), you reboot. Instead of the login screen, you get dumped into emergency mode. Red text. Failed units. A root shell prompt asking you to figure out what went wrong.
The obvious conclusion hits immediately: "NVIDIA broke my system." You just installed GPU drivers. The system was working before. Now it isn't. Cause and effect, right?
Wrong. Completely, spectacularly wrong.
Follow the Evidence, Not the Story
Paste the journalctl output and the list of failed systemctl units into AI. The prompt is straightforward: "System dropped to emergency mode after NVIDIA driver work. Here are the journal logs. What went wrong?"
Watch what happens next. AI ignores the NVIDIA narrative entirely and goes straight to the evidence. The actual errors in the journal are:
EXT4-fs (sda1): Can't find ext4 filesystem
EXT4-fs (sdb1): Can't find ext4 filesystemThose aren't GPU errors. Those are storage mount failures. The system is trying to mount /dev/sda1 and /dev/sdb1 as ext4 filesystems. But sda1 and sdb1 are LUKS encrypted containers. They don't contain ext4 filesystems directly. They contain encrypted data that, when opened through cryptsetup, exposes mapper devices that contain ext4 filesystems.
The real culprit is /etc/fstab. The fstab entries for the two external data disks reference the raw partition UUIDs with ext4 as the filesystem type. The system tries to mount encrypted blocks as if they were plain filesystems, fails (obviously), and because they're listed without nofail, systemd treats the failures as fatal and drops to emergency mode.
NVIDIA never had anything to do with it. The timing was a coincidence. The fstab was wrong the entire time; it just took a reboot to expose it.
The Correct Pattern for Encrypted Data Mounts
In /etc/crypttab: reference the raw LUKS partition UUIDs (the physical partitions like sda1, sdb1). This tells the system to open the encrypted containers at boot.
In /etc/fstab: reference the ext4 UUIDs from the opened mapper devices (/dev/mapper/cryptarchive, /dev/mapper/cryptanalysis). These are the actual filesystems you want to mount.
Always add nofail to optional data mounts. If something goes wrong with a secondary disk, your system still boots instead of dropping to emergency mode.
Never put raw encrypted partition UUIDs in fstab with ext4 as the type. The correct chain is: /dev/sda1 → LUKS open → /dev/mapper/cryptarchive → ext4 mount → /srv/archive.
The Fix
Once the real problem is visible, the solution is straightforward:
- Update
/etc/crypttabwith the correct LUKS partition UUIDs for both external disks - Update
/etc/fstabto reference the mapper device UUIDs (not the raw partition UUIDs) withnofailflags - Rebuild initramfs to pick up the crypttab changes
- Reboot
System comes up clean. NVIDIA drivers work fine. They were never the problem.
Diagnose by Evidence, Not by Timing
This is the lesson worth tattooing on your brain. Not just for this build, but for every Linux problem you'll ever face.
Just because something fails after you install a driver doesn't mean the driver caused it.
The timeline tells a convincing story: install NVIDIA drivers, reboot, emergency mode. Any reasonable person would blame the GPU drivers. The internet is full of forum posts from people who blamed the GPU drivers for exactly this kind of boot failure. And most of them probably reformatted and reinstalled when the real fix was a two-line edit in fstab.
The better approach? Read the logs. Trust the logs. Ignore the narrative.
"Correlation isn't causation, and timing isn't evidence. The journal tells you what failed. Start there, not with what you changed last."
This debugging discipline works everywhere:
- System won't boot? Check
journalctl -xbfor the actual failed units. Don't guess based on what you installed yesterday. - Service won't start? Read the service's own logs before assuming a dependency broke it.
- Network is down? Check
ip addrandsystemctl status NetworkManagerbefore blaming the firewall change you made.
Feed your logs to AI. Let it read the evidence without your narrative coloring the analysis. You'll be shocked how often the real problem has nothing to do with the thing you just changed.
The Analysis Disk Mystery
One more curveball. After fixing the archive disk mount, you try to open the analysis disk (sdb1) with cryptsetup luksOpen. The passphrase gets rejected.
This is confusing. Both external disks were set up during the Part 2 storage work. The archive disk opens fine with its passphrase. The analysis disk refuses.
Work through the possibilities methodically:
- Wrong passphrase? Possible. During a long install session with multiple LUKS containers, it's easy to mix up which passphrase goes with which disk.
- Keyboard layout mismatch? If the passphrase was set during a live USB session with a different keyboard layout than the installed system, special characters might map differently.
- Creation-time input error? When you type a passphrase blindly (no echo) during
luksFormat, a typo becomes permanent. You confirmed it once, also blindly, and moved on.
The most likely explanation is the simplest: during the initial setup, with multiple disks being encrypted in sequence, the passphrase for the analysis disk was either mistyped or confused with another one.
Here's the practical takeaway: during initial install, use simple temporary passphrases for your LUKS containers and rotate them to strong passphrases after the system is stable. That way you're not debugging passphrase mysteries at 2 AM while also trying to get your system to boot. Label your disks clearly in a physical notebook too, not just in your head.
You Just Built an OS by Hand
Take a second to appreciate what just happened here. The installer tried to destroy your storage design. You sidestepped it entirely, built the OS from raw packages with debootstrap, and ended up with a cleaner, more controlled system than any installer would have produced.
Emergency mode tried to convince you NVIDIA was the villain. You read the logs, found the real culprit hiding in fstab, and fixed it in two lines. That's the kind of debugging instinct that separates people who use Linux from people who actually understand it.
The hard infrastructure work is done. The encrypted storage is solid, the base system is running, and every boot-time failure has been tracked down and eliminated.
In Part 4, things get fun. Docker containers, the Ollama AI runtime, PostgreSQL, and yes, finally getting NVIDIA CUDA working properly so this machine can run local AI models. You've built the foundation. Now it's time to put it to work.