- modules/laptop.nix: add systemd-boot extraEntries for Windows Boot Manager Chainloads $ESP/EFI/Microsoft/Boot/bootmgfw.efi. sort-key 'z_windows' puts it after NixOS generations in the menu. Explicit entry works on all systemd-boot versions; NixOS 26.05 'Yarara' may also autodetect on shared ESP, in which case the duplicate is harmless. - install.sh: guided dual-boot installer. Shrinks Win11 NTFS with ntfsresize (no data loss), creates NixOS ext4 root, mounts shared ESP, runs nixos-install with this flake. Idempotent except for the partition shrink. - MIGRATION.md: new T490 + Windows 11 dual-boot section, with manual fallback and NixOS 26.05 compatibility notes.
199 lines
7 KiB
Markdown
199 lines
7 KiB
Markdown
# Laptop Migration: MacBook → x86_64 NixOS
|
|
|
|
## T490 + Windows 11 dual-boot (recommended flow)
|
|
|
|
The T490 already has Windows 11 installed. To add NixOS alongside it without
|
|
losing Windows:
|
|
|
|
### 1. Boot the NixOS installer from a Ventoy stick
|
|
From the live installer environment:
|
|
```bash
|
|
# Network access is required; either wifi-menu or plug in ethernet
|
|
# Then run the guided installer — it handles partitioning:
|
|
curl -L https://os.scoble.me/forgejo/sscoble/laptop-config/raw/branch/main/install.sh | sudo bash
|
|
```
|
|
|
|
The script:
|
|
- Detects `/dev/nvme0n1` and the existing ESP
|
|
- Shrinks the Windows NTFS partition with `ntfsresize` (no data loss)
|
|
- Creates a NixOS ext4 root partition in the freed space
|
|
- Mounts the existing Windows ESP at `/mnt/boot` (shared, not reformatted)
|
|
- Runs `nixos-install` with this flake
|
|
- Reboots into a systemd-boot menu with both NixOS and Windows entries
|
|
|
|
### 2. After install
|
|
First boot → systemd-boot menu shows:
|
|
- `NixOS (default)` — most recent generation
|
|
- Older NixOS generations (up to 50, configurable in `modules/laptop.nix`)
|
|
- `Windows Boot Manager` — chainloads the standard Microsoft EFI binary at
|
|
`$ESP/EFI/Microsoft/Boot/bootmgfw.efi`
|
|
|
|
If you ever want to skip the menu and boot straight to Windows, hold F12 at
|
|
the Lenovo splash and pick "Windows Boot Manager" from the firmware menu.
|
|
|
|
### 3. Fall back to manual partition if the script fails
|
|
The script handles the common case. For unusual layouts (multiple disks, LVM,
|
|
existing NixOS partitions, etc.), do it by hand:
|
|
```bash
|
|
# Identify partitions
|
|
lsblk -f
|
|
# Target the existing Windows ESP for /boot
|
|
mount /dev/nvme0n1p1 /mnt/boot # adjust to your partition
|
|
# Create NixOS root
|
|
mkfs.ext4 -L nixos /dev/nvme0n1pX
|
|
mount /dev/nvme0n1pX /mnt
|
|
nixos-generate-config --root /mnt
|
|
nixos-install --flake git+https://os.scoble.me/forgejo/sscoble/laptop-config.git#laptop --root /mnt
|
|
```
|
|
|
|
### 4. NixOS 26.05 ("Yarara") compatibility
|
|
The flake pins `nixos-unstable`, which as of June 2026 tracks NixOS 26.05+.
|
|
Key 26.05 changes that affect this config:
|
|
- `boot.initrd.systemd.enable` is now default true. Most things Just Work.
|
|
- `boot.supportedFilesystems` is now an attrset (not a list) — nix warns but
|
|
the existing config still parses.
|
|
- `systemd.extraConfig` is removed; use `systemd.settings.Manager` instead.
|
|
We don't use `systemd.extraConfig` anywhere, so this is a no-op for us.
|
|
- The new `boot.loader.systemd-boot.windows` first-class option exists for
|
|
multi-drive dual-boot, but we don't need it — sharing a single ESP means
|
|
our explicit `extraEntries` works on all systemd-boot versions.
|
|
|
|
## Order of operations
|
|
|
|
### 1. Install NixOS
|
|
Boot the NixOS ISO on the new laptop. Partition, format, mount to `/mnt`.
|
|
```bash
|
|
# Generate base config
|
|
nixos-generate-config --root /mnt
|
|
|
|
# Then rebuild from the flake (replaces generated config):
|
|
sudo nixos-rebuild switch --flake git+https://os.scoble.me/forgejo/sscoble/laptop-config.git#laptop --root /mnt
|
|
```
|
|
|
|
### 2. First build: npmDepsHash
|
|
The first `nixos-rebuild` will fail with a hash mismatch error for pi-coding-agent. The error output contains the correct hash:
|
|
```
|
|
error: hash mismatch in fixed-output derivation ...
|
|
specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
|
got: sha256-1q2w3e4r5t6y7u8i9o0p1q2w3e4r5t6y7u8i9o0p1q=
|
|
```
|
|
|
|
Copy the `got:` hash, paste into `flake.nix` → `npmDepsHash = "sha256-..."`. Commit, push, rebuild.
|
|
|
|
### 3. Transfer secrets (before reboot!)
|
|
From the MacBook, while both laptops are on the same LAN:
|
|
```bash
|
|
# SSH keys
|
|
scp ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub new-laptop:~/.ssh/
|
|
|
|
# Pi agent config (has API keys — do NOT commit to git)
|
|
scp ~/.pi/agent/SYSTEM.md new-laptop:~/.pi/agent/
|
|
scp ~/.pi/agent/models.json new-laptop:~/.pi/agent/
|
|
scp ~/.pi/agent/mcp.json new-laptop:~/.pi/agent/
|
|
scp ~/.pi/agent/settings.json new-laptop:~/.pi/agent/
|
|
scp ~/.pi/agent/auth.json new-laptop:~/.pi/agent/
|
|
|
|
# Wallpapers
|
|
scp ~/path/to/wallpaper.jpg new-laptop:~/wallpapers/default.jpg
|
|
```
|
|
|
|
### 4. Transfer projects
|
|
Push any repos not yet in Forgejo, then re-clone:
|
|
```bash
|
|
# From MacBook: push scobleclaw to Forgejo
|
|
cd ~/Projects/scobleclaw
|
|
git remote add forgejo https://os.scoble.me/forgejo/sscoble/scobleclaw.git
|
|
git push forgejo main
|
|
|
|
# From new laptop: re-clone
|
|
cd ~/Projects
|
|
git clone https://os.scoble.me/forgejo/sscoble/scobleclaw.git
|
|
git clone https://os.scoble.me/forgejo/sscoble/wgu.git
|
|
|
|
# Other projects: rsync or push individually
|
|
rsync -avz --progress ~/Projects/other-project/ new-laptop:~/Projects/other-project/
|
|
```
|
|
|
|
### 5. Transfer documents and media
|
|
```bash
|
|
# Documents (small, ~30MB)
|
|
rsync -avz --progress ~/Documents/ new-laptop:~/Documents/
|
|
|
|
# Personal media (larger — be selective)
|
|
rsync -avz --progress ~/Pictures/ new-laptop:~/Pictures/
|
|
rsync -avz --progress ~/Music/ new-laptop:~/Music/
|
|
```
|
|
|
|
### 6. Reboot and login
|
|
```bash
|
|
sudo reboot
|
|
# Login via greetd → sscoble password → Hyprland starts
|
|
# SUPER+Return → ghostty with zellij
|
|
```
|
|
|
|
### 7. Verify everything works
|
|
```bash
|
|
pi # pi agent should start
|
|
qmd status # vault search
|
|
zotero # reference manager
|
|
obsidian ~/vault # vault notes
|
|
code ~/Projects/scobleclaw # VS Code
|
|
|
|
# OB1 tunnel
|
|
ssh -fNL 3987:localhost:3987 n97-mini
|
|
curl -s http://localhost:3987/health # should return ok
|
|
|
|
# Discord: @mention Marcus in #speaker-site
|
|
```
|
|
|
|
### 8. T490 hardware setup
|
|
```bash
|
|
# Firmware updates (BIOS, EC, Thunderbolt, fingerprint reader)
|
|
fwupdmgr refresh && fwupdmgr update
|
|
# May need to disable Boot Order Lock in BIOS first
|
|
|
|
# Enroll fingerprint
|
|
fprintd-enroll
|
|
|
|
# Check sleep mode
|
|
cat /sys/power/mem_sleep
|
|
# Should show [deep] if S3 is available and configured
|
|
|
|
# Set up remote build SSH key
|
|
sudo ssh-keygen -t ed25519 -f /root/.ssh/remote-build -N ""
|
|
# Copy /root/.ssh/remote-build.pub to each cluster node's /root/.ssh/authorized_keys
|
|
|
|
# Set up Syncthing (pair with cluster nodes for vault sync)
|
|
systemctl --user enable --now syncthing
|
|
```
|
|
|
|
## Files to NOT transfer
|
|
These contain caches, build artifacts, or macOS-specific junk:
|
|
- `~/Library/` — macOS only
|
|
- `~/Applications/` — macOS apps
|
|
- `~/.ollama/` — model cache (too large, re-download)
|
|
- `~/.cargo/` — rust cache
|
|
- `~/.bun/` — bun cache
|
|
- `~/.npm/` — npm cache
|
|
- `~/go/` — go module cache
|
|
- `~/tmp/` — temp files
|
|
- `~/Downloads/` — temp downloads (clean first, then selectively transfer)
|
|
- `~/.Trash/` — obviously
|
|
|
|
## Post-migration checklist
|
|
- [ ] `pi` works
|
|
- [ ] `qmd status` works
|
|
- [ ] `vscode` opens with typst LSP
|
|
- [ ] `zotero` launches
|
|
- [ ] `obsidian ~/vault` opens vault
|
|
- [ ] `brave` launches and syncs
|
|
- [ ] SSH keys work (`ssh n97-mini`)
|
|
- [ ] OB1 tunnel works
|
|
- [ ] Hyprland: SUPER+D opens rofi, SUPER+Return opens ghostty+zellij
|
|
- [ ] Discord: @mention Marcus → gets a reply
|
|
- [ ] Wallpaper appears after login
|
|
- [ ] Firmware updated (`fwupdmgr update`)
|
|
- [ ] Fingerprint enrolled (`fprintd-enroll`)
|
|
- [ ] S3 sleep working (`cat /sys/power/mem_sleep` shows `[deep]`)
|
|
- [ ] Remote Nix builders working (`nix build --rebuild nixpkgs#hello`)
|
|
- [ ] Syncthing running for vault sync
|