Compare commits
3 Commits
63d741e55e
...
boxy-app-l
| Author | SHA1 | Date | |
|---|---|---|---|
| f8ee011d27 | |||
| 4a4ea6316d | |||
| b75c43257b |
@@ -1,284 +0,0 @@
|
||||
# NixOS /boot Partition Expansion Plan
|
||||
|
||||
## CRITICAL CONTEXT & PROBLEM STATEMENT
|
||||
|
||||
**System:** `zix790prors` - Dual-boot Windows 11 + NixOS on Samsung SSD 990 PRO 4TB
|
||||
**Problem:** Current /boot partition (/dev/nvme0n1p1) is only 100MB, insufficient for multiple NixOS generations
|
||||
**Goal:** Create 1GB /boot partition to support 20+ NixOS generations (~38MB each)
|
||||
|
||||
**Current Partition Layout:**
|
||||
```
|
||||
Device Start End Sectors Size Type
|
||||
/dev/nvme0n1p1 2048 206847 204800 100M EFI System (/boot)
|
||||
/dev/nvme0n1p2 206848 239615 32768 16M Microsoft reserved
|
||||
/dev/nvme0n1p3 239616 1953316863 1953077248 931.3G Microsoft basic data (Windows)
|
||||
/dev/nvme0n1p6 1953316864 3906394111 1953077248 931.3G Linux filesystem (/nix/store)
|
||||
/dev/nvme0n1p5 3906394112 7812548607 3906154496 1.8T Linux filesystem (/games - btrfs)
|
||||
/dev/nvme0n1p4 7812548608 7814031359 1482752 724M Windows recovery environment
|
||||
```
|
||||
|
||||
**Strategy:** Shrink /games partition (p5) by 1GB and create new 1GB EFI partition
|
||||
|
||||
---
|
||||
|
||||
## PLAN A: DETAILED STEP-BY-STEP INSTRUCTIONS
|
||||
|
||||
### PREREQUISITES
|
||||
1. **Boot from NixOS live USB** (build with `./build-liveusb.sh`)
|
||||
2. **Have Windows recovery media ready** (just in case)
|
||||
3. **Backup important data** (though this shouldn't affect user data)
|
||||
|
||||
### PHASE 1: PREPARATION & BACKUP
|
||||
|
||||
```bash
|
||||
# 1. Connect to internet
|
||||
sudo systemctl start wpa_supplicant
|
||||
# OR for ethernet: sudo systemctl start dhcpcd
|
||||
|
||||
# 2. Mount current filesystems
|
||||
sudo mkdir -p /mnt/{boot,nix,games}
|
||||
sudo mount /dev/nvme0n1p1 /mnt/boot
|
||||
sudo mount /dev/nvme0n1p6 /mnt/nix
|
||||
sudo mount /dev/nvme0n1p5 /mnt/games
|
||||
|
||||
# 3. Backup current /boot contents
|
||||
sudo mkdir -p /tmp/boot-backup
|
||||
sudo cp -a /mnt/boot/* /tmp/boot-backup/
|
||||
sudo ls -la /tmp/boot-backup/ # Verify backup
|
||||
|
||||
# 4. Record current UUIDs for later
|
||||
sudo blkid /dev/nvme0n1p1 > /tmp/original-boot-uuid.txt
|
||||
cat /tmp/original-boot-uuid.txt
|
||||
```
|
||||
|
||||
### PHASE 2: SHRINK /games PARTITION
|
||||
|
||||
```bash
|
||||
# 1. Check /games filesystem
|
||||
sudo btrfs filesystem show /mnt/games
|
||||
sudo btrfs filesystem usage /mnt/games
|
||||
|
||||
# 2. Shrink btrfs filesystem first (by 1.1GB for safety margin)
|
||||
sudo btrfs filesystem resize -1200M /mnt/games
|
||||
|
||||
# 3. Verify shrink was successful
|
||||
sudo btrfs filesystem usage /mnt/games
|
||||
|
||||
# 4. Unmount to resize partition
|
||||
sudo umount /mnt/games
|
||||
|
||||
# 5. Note current partition end sector
|
||||
sudo fdisk -l /dev/nvme0n1 | grep nvme0n1p5
|
||||
# Current: 3906394112 7812548607 (note the END: 7812548607)
|
||||
|
||||
# 6. Calculate new end sector (subtract ~2M sectors for 1GB)
|
||||
# New end should be approximately: 7812548607 - 2097152 = 7810451455
|
||||
```
|
||||
|
||||
### PHASE 3: RESIZE PARTITIONS
|
||||
|
||||
```bash
|
||||
# 1. Use parted to resize /games partition
|
||||
sudo parted /dev/nvme0n1
|
||||
|
||||
# In parted shell:
|
||||
print # Show current layout
|
||||
resizepart 5 7810451455 # Resize partition 5 to new end
|
||||
print # Verify the change
|
||||
quit
|
||||
|
||||
# 2. Verify partition table
|
||||
sudo fdisk -l /dev/nvme0n1
|
||||
```
|
||||
|
||||
### PHASE 4: CREATE NEW 1GB EFI PARTITION
|
||||
|
||||
```bash
|
||||
# 1. Create new partition in freed space
|
||||
sudo parted /dev/nvme0n1
|
||||
|
||||
# In parted shell:
|
||||
print free # Show free space
|
||||
mkpart primary fat32 7810451456 7812548607 # Create new partition
|
||||
set 7 esp on # Set ESP flag (assuming it becomes p7)
|
||||
print # Verify
|
||||
quit
|
||||
|
||||
# 2. Format new partition as FAT32
|
||||
sudo mkfs.fat -F32 -n "NIXBOOT" /dev/nvme0n1p7
|
||||
|
||||
# 3. Get new partition UUID
|
||||
sudo blkid /dev/nvme0n1p7
|
||||
```
|
||||
|
||||
### PHASE 5: COPY BOOT CONTENTS
|
||||
|
||||
```bash
|
||||
# 1. Mount new boot partition
|
||||
sudo mkdir -p /mnt/new-boot
|
||||
sudo mount /dev/nvme0n1p7 /mnt/new-boot
|
||||
|
||||
# 2. Copy all boot contents
|
||||
sudo cp -a /tmp/boot-backup/* /mnt/new-boot/
|
||||
|
||||
# 3. Verify copy
|
||||
sudo ls -la /mnt/new-boot/
|
||||
sudo du -sh /mnt/new-boot/*
|
||||
```
|
||||
|
||||
### PHASE 6: UPDATE CONFIGURATION
|
||||
|
||||
```bash
|
||||
# 1. Mount root filesystem
|
||||
sudo mount /dev/nvme0n1p6 /mnt/nix
|
||||
sudo mount /dev/nvme0n1p6 /mnt # For nixos-enter
|
||||
|
||||
# 2. Mount other needed filesystems for chroot
|
||||
sudo mount --bind /dev /mnt/dev
|
||||
sudo mount --bind /proc /mnt/proc
|
||||
sudo mount --bind /sys /mnt/sys
|
||||
sudo mount /dev/nvme0n1p7 /mnt/boot # Mount NEW boot partition
|
||||
|
||||
# 3. Enter NixOS environment
|
||||
sudo nixos-enter
|
||||
|
||||
# 4. Inside chroot - update hardware configuration
|
||||
# Edit /etc/nixos/hardware-configuration.nix or check current fstab
|
||||
NEW_UUID=$(blkid /dev/nvme0n1p7 | grep -o 'UUID="[^"]*"' | cut -d'"' -f2)
|
||||
echo "New boot UUID: $NEW_UUID"
|
||||
|
||||
# 5. Update boot partition UUID in configuration
|
||||
# This step depends on your current setup - likely in hardware-configuration.nix
|
||||
```
|
||||
|
||||
### PHASE 7: REGENERATE BOOTLOADER
|
||||
|
||||
```bash
|
||||
# Still in nixos-enter chroot:
|
||||
|
||||
# 1. Rebuild bootloader configuration
|
||||
nixos-rebuild switch --install-bootloader
|
||||
|
||||
# 2. Exit chroot
|
||||
exit
|
||||
|
||||
# 3. Update EFI boot variables (if needed)
|
||||
sudo efibootmgr -v # Check current entries
|
||||
```
|
||||
|
||||
### PHASE 8: VERIFICATION & CLEANUP
|
||||
|
||||
```bash
|
||||
# 1. Unmount everything
|
||||
sudo umount /mnt/new-boot
|
||||
sudo umount /mnt/boot
|
||||
sudo umount /mnt/nix
|
||||
sudo umount /mnt/games
|
||||
|
||||
# 2. Remount /games with new partition size
|
||||
sudo mount /dev/nvme0n1p5 /mnt/games
|
||||
sudo btrfs filesystem resize max /mnt/games # Expand btrfs to use available space
|
||||
|
||||
# 3. Final verification
|
||||
sudo fdisk -l /dev/nvme0n1
|
||||
sudo lsblk
|
||||
```
|
||||
|
||||
### PHASE 9: TEST BOOT
|
||||
|
||||
```bash
|
||||
# 1. Reboot and test
|
||||
sudo reboot
|
||||
|
||||
# 2. After successful boot, update NixOS config
|
||||
# Change configurationLimit to 20 (perfect for 1GB /boot)
|
||||
# In /home/johno/nixos-configs/machines/zix790prors/configuration.nix:
|
||||
# boot.loader.systemd-boot.configurationLimit = 20;
|
||||
|
||||
# 3. Test rebuild
|
||||
sudo nixos-rebuild switch --flake .#zix790prors
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PLAN B: TROUBLESHOOTING & CONTEXT FOR ADDITIONAL HELP
|
||||
|
||||
### IF THINGS GO WRONG
|
||||
|
||||
**Scenario 1: System won't boot**
|
||||
- Boot from live USB
|
||||
- Mount old boot partition: `sudo mount /dev/nvme0n1p1 /mnt/boot`
|
||||
- Check if bootloader entries exist: `ls /mnt/boot/loader/entries/`
|
||||
- Regenerate from live USB using `nixos-install` with `--root /mnt`
|
||||
|
||||
**Scenario 2: Partition operations fail**
|
||||
- **STOP IMMEDIATELY**
|
||||
- Document exact error message
|
||||
- Use `sudo fdisk -l` and `sudo parted /dev/nvme0n1 print` to check current state
|
||||
- Do NOT proceed without understanding the error
|
||||
|
||||
**Scenario 3: Btrfs resize fails**
|
||||
- Check available space: `sudo btrfs filesystem usage /mnt/games`
|
||||
- May need to balance first: `sudo btrfs balance start /mnt/games`
|
||||
- Or defragment: `sudo btrfs filesystem defragment -r /mnt/games`
|
||||
|
||||
### RECOVERY INFORMATION
|
||||
|
||||
**Original partition layout (before changes):**
|
||||
```
|
||||
/dev/nvme0n1p1: 100M EFI (/boot) - sectors 2048-206847
|
||||
/dev/nvme0n1p5: 1.8T btrfs (/games) - sectors 3906394112-7812548607
|
||||
```
|
||||
|
||||
**Key files to check:**
|
||||
- `/etc/fstab` - filesystem mount configuration
|
||||
- `/etc/nixos/hardware-configuration.nix` - NixOS hardware config
|
||||
- `/boot/loader/loader.conf` - systemd-boot config
|
||||
|
||||
**Important UUIDs (save these before starting):**
|
||||
```bash
|
||||
# Run these BEFORE making changes:
|
||||
sudo blkid /dev/nvme0n1p1 # Current boot partition
|
||||
sudo blkid /dev/nvme0n1p5 # Games partition
|
||||
sudo blkid /dev/nvme0n1p6 # Nix store partition
|
||||
```
|
||||
|
||||
**Emergency boot options:**
|
||||
- Boot from Windows (should be unaffected)
|
||||
- Boot from NixOS live USB
|
||||
- Use systemd-boot menu to select older generation (if available)
|
||||
|
||||
### ALTERNATIVE APPROACHES IF PLAN A FAILS
|
||||
|
||||
1. **Expand into Microsoft reserved instead** (lower risk, less space)
|
||||
2. **Use external USB for /boot** (temporary solution)
|
||||
3. **Recreate partition table** (nuclear option, requires full backup)
|
||||
|
||||
---
|
||||
|
||||
## TECHNICAL DETAILS FOR CLAUDE CODE ASSISTANCE
|
||||
|
||||
**Repository:** `/home/johno/nixos-configs` - NixOS flake-based configuration
|
||||
**Machine config:** `machines/zix790prors/configuration.nix`
|
||||
**Current configurationLimit:** 1 (temporarily reduced from 2)
|
||||
**Target configurationLimit:** 20 (requires ~1GB /boot)
|
||||
|
||||
**Key commands for status checking:**
|
||||
```bash
|
||||
# Partition info
|
||||
sudo fdisk -l /dev/nvme0n1
|
||||
sudo parted /dev/nvme0n1 print free
|
||||
sudo lsblk
|
||||
|
||||
# Filesystem info
|
||||
sudo btrfs filesystem show
|
||||
sudo btrfs filesystem usage /games
|
||||
df -h /boot
|
||||
|
||||
# Boot info
|
||||
sudo efibootmgr -v
|
||||
ls -la /boot/loader/entries/
|
||||
```
|
||||
|
||||
**This document created:** 2025-10-02 by Claude Code
|
||||
**Hardware:** Samsung SSD 990 PRO 4TB, NixOS + Windows 11 dual boot
|
||||
@@ -34,7 +34,7 @@ with lib;
|
||||
|
||||
# Use the systemd-boot EFI boot loader.
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.systemd-boot.configurationLimit = 1; # Temporarily reduced to 1 to free /boot space
|
||||
boot.loader.systemd-boot.configurationLimit = 20;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
boot.loader.timeout = 10;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
boot.extraModulePackages = [ ];
|
||||
|
||||
fileSystems."/boot" =
|
||||
{ device = "/dev/disk/by-uuid/76B0-738E";
|
||||
{ device = "/dev/disk/by-uuid/11C1-EB58";
|
||||
fsType = "vfat";
|
||||
options = [ "fmask=0077" "dmask=0077" ];
|
||||
};
|
||||
|
||||
104
packages/app-launcher-server/app-launcher-server.py
Normal file
104
packages/app-launcher-server/app-launcher-server.py
Normal file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from urllib.parse import urlparse
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Allowlisted applications that can be launched
|
||||
ALLOWED_APPS = {
|
||||
'firefox': 'firefox',
|
||||
'kodi': 'kodi'
|
||||
}
|
||||
|
||||
class AppLauncherHandler(BaseHTTPRequestHandler):
|
||||
def log_message(self, format, *args):
|
||||
logger.info(format % args)
|
||||
|
||||
def do_GET(self):
|
||||
if self.path == '/':
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.end_headers()
|
||||
response = {
|
||||
'status': 'running',
|
||||
'available_apps': list(ALLOWED_APPS.keys()),
|
||||
'usage': 'POST /launch/<app_name> to launch an application'
|
||||
}
|
||||
self.wfile.write(json.dumps(response, indent=2).encode())
|
||||
else:
|
||||
self.send_error(404)
|
||||
|
||||
def do_POST(self):
|
||||
parsed_path = urlparse(self.path)
|
||||
path_parts = parsed_path.path.strip('/').split('/')
|
||||
|
||||
if len(path_parts) == 2 and path_parts[0] == 'launch':
|
||||
app_name = path_parts[1]
|
||||
self.launch_app(app_name)
|
||||
else:
|
||||
self.send_error(404, "Invalid endpoint. Use /launch/<app_name>")
|
||||
|
||||
def launch_app(self, app_name):
|
||||
if app_name not in ALLOWED_APPS:
|
||||
self.send_error(400, f"Application '{app_name}' not allowed. Available apps: {list(ALLOWED_APPS.keys())}")
|
||||
return
|
||||
|
||||
command = ALLOWED_APPS[app_name]
|
||||
|
||||
try:
|
||||
# Launch the application in the background
|
||||
# Ensure we have the proper environment for GUI apps
|
||||
env = os.environ.copy()
|
||||
|
||||
logger.info(f"Launching application: {command}")
|
||||
process = subprocess.Popen(
|
||||
[command],
|
||||
env=env,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
start_new_session=True
|
||||
)
|
||||
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.end_headers()
|
||||
response = {
|
||||
'status': 'success',
|
||||
'message': f'Successfully launched {app_name}',
|
||||
'pid': process.pid
|
||||
}
|
||||
self.wfile.write(json.dumps(response).encode())
|
||||
|
||||
except FileNotFoundError:
|
||||
logger.error(f"Application not found: {command}")
|
||||
self.send_error(500, f"Application '{app_name}' not found on system")
|
||||
except Exception as e:
|
||||
logger.error(f"Error launching {command}: {e}")
|
||||
self.send_error(500, f"Failed to launch {app_name}: {str(e)}")
|
||||
|
||||
def main():
|
||||
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8081
|
||||
|
||||
server = HTTPServer(('0.0.0.0', port), AppLauncherHandler)
|
||||
logger.info(f"App launcher server starting on port {port}")
|
||||
logger.info(f"Available applications: {list(ALLOWED_APPS.keys())}")
|
||||
|
||||
try:
|
||||
server.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Server shutting down...")
|
||||
server.server_close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
5
packages/app-launcher-server/default.nix
Normal file
5
packages/app-launcher-server/default.nix
Normal file
@@ -0,0 +1,5 @@
|
||||
{ pkgs }:
|
||||
|
||||
pkgs.writeShellScriptBin "app-launcher-server" ''
|
||||
exec ${pkgs.python3}/bin/python3 ${./app-launcher-server.py} "$@"
|
||||
''
|
||||
@@ -2,4 +2,5 @@
|
||||
{
|
||||
vulkanHDRLayer = pkgs.callPackage ./vulkan-hdr-layer {};
|
||||
tea-rbw = pkgs.callPackage ./tea-rbw {};
|
||||
app-launcher-server = pkgs.callPackage ./app-launcher-server {};
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ with lib;
|
||||
|
||||
let
|
||||
cfg = config.roles.kodi;
|
||||
customPkgs = pkgs.callPackage ../../packages {};
|
||||
in
|
||||
{
|
||||
options.roles.kodi = {
|
||||
@@ -14,6 +15,18 @@ in
|
||||
wayland = mkOption {
|
||||
default = true;
|
||||
};
|
||||
appLauncherServer = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable HTTP app launcher server for remote control";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.int;
|
||||
default = 8081;
|
||||
description = "Port for the app launcher HTTP server";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -33,17 +46,35 @@ in
|
||||
};
|
||||
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ 8080 ];
|
||||
allowedTCPPorts = [ 8080 ] ++ optional cfg.appLauncherServer.enable cfg.appLauncherServer.port;
|
||||
allowedUDPPorts = [ 8080 ];
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
kodiPkg
|
||||
wget
|
||||
];
|
||||
firefox
|
||||
] ++ optional cfg.appLauncherServer.enable customPkgs.app-launcher-server;
|
||||
|
||||
programs.kdeconnect.enable = true;
|
||||
|
||||
systemd.user.services = mkIf cfg.appLauncherServer.enable {
|
||||
app-launcher-server = {
|
||||
description = "HTTP App Launcher Server";
|
||||
wantedBy = [ "graphical-session.target" ];
|
||||
after = [ "graphical-session.target" ];
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
ExecStart = "${customPkgs.app-launcher-server}/bin/app-launcher-server ${toString cfg.appLauncherServer.port}";
|
||||
Restart = "always";
|
||||
RestartSec = "5s";
|
||||
Environment = [
|
||||
"PATH=${pkgs.firefox}/bin:${kodiPkg}/bin:/run/current-system/sw/bin"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services = if cfg.autologin then {
|
||||
displayManager = {
|
||||
autoLogin.enable = true;
|
||||
|
||||
Reference in New Issue
Block a user