NixOS Migration Guide: Arch Linux to NixOS
This guide documents the migration from Arch Linux to NixOS on powerhouse, with Windows dual-boot.
Overview
- Source System: Arch Linux with LVM+LUKS on 2x 1.8TB NVMe drives
- Target System: NixOS with LUKS+Btrfs on nvme1n1, Windows on nvme0n1
- Dual-boot: systemd-boot with Windows chainloading
- Backup Strategy: Restic to Google Cloud Storage (pre-requisite)
Pre-Migration Checklist
1. Backup Current System
Before proceeding, ensure complete backups:
# Verify restic backup is up to date
restic -r gs:powerhouse-backup:/ snapshots
# Run fresh backup
restic -r gs:powerhouse-backup:/ backup /home/brancengregory \
--exclude="/home/brancengregory/.cache" \
--exclude="/home/brancengregory/Downloads" \
--exclude="/home/brancengregory/.local/share/Trash"
# Verify backup integrity
restic -r gs:powerhouse-backup:/ check
2. Export Configuration Data
# Create export directory
mkdir -p ~/migration-exports
# SSH keys
cp -r ~/.ssh ~/migration-exports/
# GPG keys - NOTE: Secret keys are on Nitrokey hardware tokens
# Only export public keys and trust database (no private keys)
gpg --export --armor > ~/migration-exports/gpg-public-keys.asc
cp ~/.gnupg/trustdb.gpg ~/migration-exports/ 2>/dev/null || true
# DO NOT export secret keys - they remain on hardware token
# Browser profiles (bookmarks, extensions, etc.)
# Firefox
cp -r ~/.mozilla ~/migration-exports/ 2>/dev/null || true
# Chrome/Chromium
cp -r ~/.config/google-chrome ~/migration-exports/ 2>/dev/null || true
cp -r ~/.config/chromium ~/migration-exports/ 2>/dev/null || true
# Document any manually installed packages
pacman -Qe > ~/migration-exports/explicit-packages.txt
pacman -Qm > ~/migration-exports/aur-packages.txt
# Copy exports to external storage or restic backup
restic -r gs:powerhouse-backup:/ backup ~/migration-exports
3. Gather System Information
# Save current partition layout
lsblk -f > ~/migration-exports/lsblk-layout.txt
fdisk -l > ~/migration-exports/fdisk-layout.txt
# Save network configuration
ip addr > ~/migration-exports/network-config.txt
cat /etc/resolv.conf > ~/migration-exports/resolv.conf
# Save custom systemd services
ls -la /etc/systemd/system/ > ~/migration-exports/custom-services.txt
# Save cron jobs
crontab -l > ~/migration-exports/crontab.txt 2>/dev/null || true
sudo crontab -l > ~/migration-exports/root-crontab.txt 2>/dev/null || true
Phase 1: Windows Installation
1.1 Prepare Windows Installation
- Download Windows 11 ISO from Microsoft
- Create bootable USB with Ventoy or Rufus
- Backup Windows product key (if applicable)
1.2 Install Windows on nvme0n1
Boot from Windows USB:
- Boot into Windows installer
- Select "Custom: Install Windows only (advanced)"
- Important: Only select nvme0n1 (Drive 0), NOT nvme1n1
Partition Layout for nvme0n1:
Disk 0 (nvme0n1) - 1.8TB
├── System Partition (100MB) - EFI
├── MSR (16MB)
├── Windows (1024GB) - C: drive
└── Unallocated (~780GB) - Future use
Installation Steps:
- Delete all partitions on Disk 0
- Select unallocated space, click "New"
- Windows will create necessary partitions
- Adjust C: drive to 1024000 MB (1TB)
- Leave remaining ~780GB unallocated
- Install Windows to the 1TB partition
1.3 Post-Windows Setup
- Complete Windows setup (skip Microsoft account if desired)
- Install drivers (GPU, chipset, etc.)
- Enable BitLocker if desired (on Windows drive only)
- IMPORTANT: Note the PARTUUID of the Windows ESP partition
# In Windows PowerShell (Admin)
diskpart
list disk
select disk 0
list partition
select partition 1 # Usually the EFI partition
detail partition
# Note the "Partition UUID" value
Phase 2: NixOS Installation Preparation
2.1 Generate SSH Host Key
Before installation, generate the powerhouse SSH host key:
# On your current Arch system or another Linux machine
mkdir -p ~/powerhouse-ssh-key
cd ~/powerhouse-ssh-key
# Generate host key
ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N "" -C "powerhouse-host-key"
# The files will be:
# - ssh_host_ed25519_key (private)
# - ssh_host_ed25519_key.pub (public)
2.2 Add SSH Key to Secrets
# Copy public key to this repo's secrets directory
cp ~/powerhouse-ssh-key/ssh_host_ed25519_key.pub /path/to/nix-config/secrets/powerhouse_host_key.pub
# Add private key to secrets.yaml using sops
cd /path/to/nix-config
# Edit secrets.yaml
sops secrets/secrets.yaml
# Add new entries:
# ssh:
# host_key: |
# -----BEGIN OPENSSH PRIVATE KEY-----
# ... (paste contents of ssh_host_ed25519_key)
# -----END OPENSSH PRIVATE KEY-----
# host_key_pub: ssh-ed25519 AAAAC3NzaC... brancengregory@powerhouse
2.3 Update Configuration
Update the Windows ESP PARTUUID in hosts/powerhouse/config.nix:
fileSystems."/boot/efi-windows" = {
device = "/dev/disk/by-partuuid/XXXX-XXXX"; # Replace with actual PARTUUID
fsType = "vfat";
options = ["noauto" "x-systemd.automount"];
};
2.4 Build NixOS Installer
Create a custom NixOS installer with your flake:
cd /path/to/nix-config
# Build the installer ISO
nix build .#nixosConfigurations.powerhouse.config.system.build.isoImage
# Or download standard NixOS ISO and use manually
Phase 3: NixOS Installation
3.1 Boot NixOS Installer
- Boot from NixOS USB
- Select "NixOS Installer" from boot menu
- Connect to internet (WiFi or Ethernet)
3.2 Prepare Installation Environment
# Set up networking (if WiFi)
nmtui
# Clone your flake (or use local copy)
git clone https://github.com/brancengregory/nix-config.git /mnt/nix-config
cd /mnt/nix-config
# Or copy from USB/mounted drive
cp -r /path/to/nix-config /mnt/
3.3 Run Disko Partitioning
WARNING: This will DESTROY all data on nvme1n1!
cd /mnt/nix-config
# Dry run first to verify layout
sudo nix run github:nix-community/disko -- --mode disko hosts/powerhouse/disks/main.nix --dry-run
# If layout looks correct, run for real
sudo nix run github:nix-community/disko -- --mode disko hosts/powerhouse/disks/main.nix
# Verify partitions
lsblk
3.4 Generate Hardware Configuration
# Mount the new root
sudo mount /dev/mapper/crypted /mnt
sudo mount /dev/nvme1n1p1 /mnt/boot
# Generate hardware config
sudo nixos-generate-config --root /mnt
# Copy generated hardware config to your flake
cp /mnt/etc/nixos/hardware-configuration.nix hosts/powerhouse/hardware-generated.nix
# Compare with existing and merge any new modules
# Then update hardware.nix if needed
3.5 Install NixOS
# Install NixOS with your flake
sudo nixos-install --flake .#powerhouse
# Set root password
sudo passwd
# Reboot
sudo reboot
Phase 4: Post-Installation
4.1 Initial Setup
After first boot:
# Set user password
sudo passwd brancengregory
# Verify boot entries
sudo bootctl list
# Check all filesystems are mounted correctly
df -h
lsblk
4.2 Configure Bootloader for Windows
If Windows doesn't appear in boot menu:
# Mount Windows ESP
sudo mkdir -p /boot/efi-windows
sudo mount /dev/nvme0n1p1 /boot/efi-windows # Adjust partition number
# Copy Windows bootloader
sudo mkdir -p /boot/EFI/Microsoft/Boot
sudo cp -r /boot/efi-windows/EFI/Microsoft/Boot/* /boot/EFI/Microsoft/Boot/
# Update systemd-boot
sudo bootctl update
# Reboot and check
sudo reboot
4.3 Restore Data from Backup
# Install restic if not already present
# (should be in your home.nix)
# Restore home directory
restic -r gs:powerhouse-backup:/ restore latest --target /tmp/restore --include "/home/brancengregory"
# Copy files to proper location
sudo cp -r /tmp/restore/home/brancengregory/* /home/brancengregory/
sudo chown -R brancengregory:users /home/brancengregory
# Restore migration exports
restic -r gs:powerhouse-backup:/ restore latest --target /tmp/migration --include "/migration-exports"
4.4 Restore SSH Keys and Provision GPG
# Restore SSH keys
mkdir -p ~/.ssh
chmod 700 ~/.ssh
cp /tmp/migration/migration-exports/.ssh/* ~/.ssh/
chmod 600 ~/.ssh/*
chmod 644 ~/.ssh/*.pub 2>/dev/null || true
# GPG: Insert Nitrokey hardware token
# Stubs are created automatically - no key import needed
gpg --card-edit
# gpg/card> fetch # Downloads public key from keyserver
# gpg/card> quit
# Link hardware (creates stubs)
gpg-connect-agent "scd serialno" "learn --force" /bye
# Verify
gpg --list-secret-keys # Should show 'ssb>' (stub notation)
ssh-add -L | grep cardno # Should show SSH key
4.5 Verify Configuration
# Check snapper is working
snapper list
snapper list --config home
# Verify NVIDIA drivers
nvidia-smi
# Check Btrfs subvolumes
sudo btrfs subvolume list /
# Verify swap
swapon -s
free -h
# Test boot entries
sudo bootctl list
Phase 5: Final Configuration
5.1 Update Flake with Real Hardware Config
After installation, update the repository:
cd ~/nix-config # or wherever you cloned it
# Update hardware.nix with any changes from install
# Update secrets with powerhouse SSH host key
# Commit changes
git add .
git commit -m "Update powerhouse config after installation"
git push
5.2 Enable Automatic Backups
Ensure restic backup service is working:
# Check service status
systemctl status restic-backups-daily-home
# Test backup manually
sudo systemctl start restic-backups-daily-home
sudo journalctl -u restic-backups-daily-home -f
# Verify backup in cloud
restic -r gs:powerhouse-backup:/ snapshots
Troubleshooting
Windows Not Showing in Boot Menu
# Manually add Windows entry
sudo mkdir -p /boot/loader/entries
sudo tee /boot/loader/entries/windows.conf <<EOF
title Windows 11
efi /EFI/Microsoft/Boot/bootmgfw.efi
EOF
# Or copy bootloader files
sudo mkdir -p /boot/EFI/Microsoft/Boot
sudo cp /boot/efi-windows/EFI/Microsoft/Boot/bootmgfw.efi /boot/EFI/Microsoft/Boot/
sudo cp /boot/efi-windows/EFI/Microsoft/Boot/*.efi /boot/EFI/Microsoft/Boot/
LUKS Boot Issues
If system doesn't prompt for LUKS password:
# From NixOS installer, chroot into system
sudo mount /dev/mapper/crypted /mnt
sudo mount /dev/nvme1n1p1 /mnt/boot
sudo nixos-enter
# Check initrd configuration
cat /etc/nixos/configuration.nix | grep -A5 "initrd"
# Ensure cryptd is in initrd
boot.initrd.luks.devices."crypted".device = "/dev/disk/by-uuid/...";
NVIDIA Driver Issues
If graphical session fails to start:
# Boot to multi-user.target (no GUI)
systemctl isolate multi-user.target
# Check NVIDIA module
modinfo nvidia
# Check X11 logs
journalctl -u display-manager -b
# Try disabling modesetting temporarily
# Edit config to set: hardware.nvidia.modesetting.enable = false;
BTRFS Subvolume Issues
# Check subvolumes
sudo btrfs subvolume list /
# If @home not mounted correctly
sudo umount /home
sudo mount -o subvol=@home,compress=zstd,noatime /dev/mapper/crypted /home
# Update /etc/fstab or fix in configuration.nix
Rollback Plan
If migration fails catastrophically:
- Boot Arch Linux live USB
- Unlock LUKS volumes:
cryptsetup open /dev/nvme0n1p2 cryptkeeper - Mount and access data
- Restore from restic backup if needed
- Reinstall GRUB if needed:
mount /dev/mapper/cryptkeeper-root /mnt mount /dev/nvme0n1p1 /mnt/boot arch-chroot /mnt grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB grub-mkconfig -o /boot/grub/grub.cfg
Post-Migration Checklist
- System boots successfully
- Windows boots from systemd-boot menu
- LUKS password prompt works
- All Btrfs subvolumes mounted correctly
- NVIDIA drivers loaded
- Plasma desktop starts
-
Snapper snapshots working (test:
snapper create -d "test") - Home directory restored with correct permissions
-
SSH keys working (test:
ssh -T git@github.com) -
GPG hardware token working (test:
gpg --card-status,ssh-add -L) - Restic backup configured and tested
- All critical applications installed and working
- Firefox/Chrome profiles restored
- Network configuration working (WiFi, VPN, etc.)
- Printer/scanner working (if applicable)
- Bluetooth devices paired
Notes
- Keep Arch installation USB accessible during first week
- Monitor disk usage on new Btrfs layout:
btrfs filesystem df / - Test snapper rollback:
sudo snapper rollback - Document any manual tweaks needed after install
- Update this guide with lessons learned