Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MiniBook X Kubuntu Install

Installing Kubuntu 24.04 on a Chuwi MiniBook X N100 — entirely over SSH from a MacBook, with zero physical interaction beyond opening the lid and fixing the firewall.

Built through agentic coding with Claude Code. The AI operated the MiniBook X remotely via SSH to Windows, used a homelab Linux node to build the root filesystem, streamed a compressed disk image over HTTP, wrote it raw to an NVMe partition from Python on Windows, and booted into a working Kubuntu desktop — all without touching the machine.


For prior art on Linux compatibility for this device, see sonnyp/linux-minibook-x.


The Problem

The Chuwi MiniBook X N100 ships with Windows 11 IoT Enterprise LTSC. The goal: dual-boot Kubuntu 24.04 LTS alongside Windows, with full remote SSH access to both OSes, and zero reliance on physical interaction after initial setup.

The constraints:

  • No USB boot — the BIOS refused to boot from a USB SATA SSD
  • No Ethernet — only WiFi, and the USB-Ethernet dongle was missing
  • No IPKVM — no HDMI port, no USB-HDMI adapter available
  • No WSL — Windows 11 IoT Enterprise LTSC has no Microsoft Store; wsl --install fails even after enabling the features
  • Only SSH to Windows — that's all we had to work with

The question: how do you install Linux on a machine when your only interface is SSH to the existing Windows install?

The Machine

Spec Detail
CPU Intel N100 (4C/4T, 3.4GHz turbo)
RAM 12GB LPDDR5
Storage 512GB NVMe (KPART512GBC2DVT)
Display 10.51" DSI, 1200x1920 native (portrait), 50Hz
WiFi AX (2.4GHz + 5GHz)
OS (original) Windows 11 IoT Enterprise LTSC 2024
Form factor 360° YOGA 2-in-1, 920g

The Approach: Debootstrap from Windows

The key insight: never leave Windows SSH. Build the entire Linux root filesystem on a remote machine, then write it directly to the disk from Windows.

┌──────────┐  SSH   ┌──────────────┐  HTTP    ┌──────────┐
│  MacBook  │──────→│  MiniBook X   │←────────│ .68 node  │
│ (control) │       │  (Windows)    │  gzip'd │ (deboot-  │
│           │       │  Python→disk  │  image  │  strap)   │
└──────────┘       └──────────────┘         └──────────┘

Phase 0: Establish Access

The first challenge was just getting SSH working. Windows had OpenSSH server installed but the firewall was blocking port 22, and the sshd service wasn't set to auto-start. After the user opened the firewall manually from the console:

# From SSH, immediately persist access
Set-Service -Name sshd -StartupType Automatic
New-NetFirewallRule -Name "OpenSSH-Server-Inbound" -DisplayName "OpenSSH Server (sshd) Inbound" `
    -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
# Set PowerShell as default SSH shell
New-ItemProperty -Path 'HKLM:\SOFTWARE\OpenSSH' -Name DefaultShell `
    -Value 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' -PropertyType String -Force

This was the single most important step. Everything after this was fully remote.

Phase 1: Create the Partition

From Windows SSH, used diskpart to carve 128GB from the unallocated space (user had pre-shrunk the Windows partition):

Disk 0 (512GB NVMe):
  Partition 1: 100MB  EFI System Partition (FAT32), UUID 24BF-0BF8
  Partition 2: 100MB  Microsoft Reserved
  Partition 3: 347GB  Windows C: (NTFS)
  Partition 4: 127GB  ← NEW: Kubuntu root (ext4), UUID d26b99a6-fa4b-473d-a5ee-051905693236
  Partition 5: 1.9GB  Windows Recovery

Phase 2: Debootstrap on a Homelab Node

Windows can't format ext4, and WSL wouldn't install on IoT LTSC. So the root filesystem was built on .68 — a homelab Ubuntu node with 356GB free:

# On .68
sudo debootstrap --arch=amd64 noble /tmp/kubuntu-rootfs http://archive.ubuntu.com/ubuntu

Then chrooted in to install everything needed for a bootable system with remote access:

sudo chroot /tmp/kubuntu-rootfs
apt install linux-image-generic linux-firmware openssh-server \
    network-manager grub-efi-amd64-bin grub-efi-amd64-signed shim-signed sudo

Pre-configured before first boot:

  • WiFi: NetworkManager .nmconnection files for both 2.4GHz (priority) and 5GHz (fallback)
  • SSH: systemctl enable ssh, password auth on
  • User: evnchn with sudo NOPASSWD
  • GRUB: video=DSI-1:panel_orientation=right_side_up kernel parameter
  • Hostname, timezone (Asia/Hong_Kong), locale (en_US.UTF-8)

Phase 3: The Transfer

The rootfs was written into a 127GB ext4 disk image on .68:

truncate -s 136365211648 /tmp/kubuntu.img    # sparse, instant
mkfs.ext4 -L kubuntu /tmp/kubuntu.img
mount -o loop /tmp/kubuntu.img /tmp/kubuntu-mnt
cd /tmp/kubuntu-mnt && tar xzf /tmp/kubuntu-rootfs.tar.gz

Compressed with parallel gzip (2.4GB compressed from 127GB sparse — mostly zeros):

pigz -1 -c /tmp/kubuntu.img > /tmp/kubuntu.img.gz   # ~2 minutes

Served via HTTP:

python3 -m http.server 9999 --directory /tmp

On the MiniBook X, a Python script downloaded and wrote the raw image directly to the partition:

# write_partition.py — downloads gzipped image, writes raw to physical disk
import gzip, urllib.request
disk = open(r"\\.\PhysicalDrive0", "r+b", buffering=0)
disk.seek(372667056128)  # partition 4 offset
resp = urllib.request.urlopen("http://192.168.50.68:9999/kubuntu.img.gz")
decompressor = gzip.GzipFile(fileobj=resp)
while chunk := decompressor.read(4*1024*1024):
    disk.write(chunk)

127GB written over WiFi in ~20 minutes.

Phase 4: Bootloader

The EFI System Partition was mounted on Windows (mountvol S: /S) and GRUB files were downloaded from .68:

S:\EFI\ubuntu\shimx64.efi    — Secure Boot shim
S:\EFI\ubuntu\grubx64.efi    — GRUB EFI binary
S:\EFI\ubuntu\grub.cfg       — Points GRUB to /boot/grub on the ext4 partition

A UEFI boot entry was created via bcdedit:

bcdedit /copy "{bootmgr}" /d "Ubuntu"
bcdedit /set {new-id} path \EFI\ubuntu\shimx64.efi

The fallback bootloader (\EFI\Boot\bootx64.efi) was also replaced with the GRUB shim as a safety net — some UEFI firmware ignores the boot order and always loads the fallback path.

Phase 5: First Boot

bcdedit /set "{fwbootmgr}" bootsequence "{ubuntu-id}"
Restart-Computer -Force

30 seconds later:

$ ssh evnchn@192.168.50.164
minibook-x
Linux minibook-x 6.8.0-106-generic #106-Ubuntu SMP x86_64

WiFi connected automatically. SSH was waiting. Zero physical interaction.

Phase 6: Desktop

With SSH confirmed, the desktop was installed:

sudo apt install kubuntu-desktop    # ~30 minutes on WiFi
sudo systemctl enable sddm
sudo systemctl set-default graphical.target

Post-Install Fixes

Eight fixes were needed to go from "it boots" to "it works." This is the trial install — all fixes documented here for replay on a future fresh Kubuntu install.

Fix 1: Screen Rotation

Problem: The DSI panel is natively portrait (1200x1920). Boots sideways in the landscape-oriented chassis.

The kernel parameter video=DSI-1:panel_orientation=right_side_up is set but not sufficient for X11 — it helps the kernel console but X/KDE need their own rotation.

Files changed:

/etc/X11/xorg.conf.d/10-monitor.conf:

Section "Monitor"
    Identifier "DSI-1"
    Option "Rotate" "right"
EndSection

/etc/sddm.conf.d/rotate.conf:

[X11]
DisplayCommand=/etc/sddm/Xsetup

The SDDM Xsetup script is shared with Fix 7 (touchscreen) — see below.

Fix 2: Power Management

Problem: Stock Kubuntu doesn't run powertop auto-tune by default.

Measurements (idle, KDE desktop, WiFi, 50% backlight):

Metric Value
CPU package (RAPL) 0.61W
Total system (battery) 4.24W
Estimated battery life ~6.3hr on 26.6Wh

Governor is already powersave. KDE power profile is power-saver (EPP=power). Switching profiles changes CPU power (0.5W → 3W) but not total system draw at idle.

Files changed:

/etc/systemd/system/powertop-autotune.service:

[Unit]
Description=PowerTOP auto-tune
After=multi-user.target

[Service]
Type=oneshot
ExecStart=/usr/sbin/powertop --auto-tune
RemainAfterExit=true

[Install]
WantedBy=multi-user.target
sudo apt install powertop
sudo systemctl enable powertop-autotune

Marginal gains — most power is display/WiFi/platform, not tuneable software knobs.

Fix 3: GRUB Boot Order

Problem: UEFI firmware always boots Windows. The one-shot bootsequence was consumed after first boot, and subsequent reboots went straight to Windows.

Fix (two layers):

  1. Made GRUB first in UEFI boot order:
bcdedit /set "{fwbootmgr}" displayorder "{ubuntu-id}" "{bootmgr}"
  1. Replaced the UEFI fallback bootloader with GRUB's shim:
Copy-Item S:\EFI\Boot\bootx64.efi S:\EFI\Boot\bootx64.efi.bak  # backup
Copy-Item S:\EFI\ubuntu\shimx64.efi S:\EFI\Boot\bootx64.efi
Copy-Item S:\EFI\ubuntu\grubx64.efi S:\EFI\Boot\grubx64.efi
Copy-Item S:\EFI\ubuntu\grub.cfg S:\EFI\Boot\grub.cfg

To restore Windows-only boot: Copy-Item S:\EFI\Boot\bootx64.efi.bak S:\EFI\Boot\bootx64.efi

Fix 4: Apt Sources

Problem: Debootstrap created a single combined sources stanza. Stock Kubuntu uses a separate security.ubuntu.com stanza and includes noble-backports.

Fix: Replaced /etc/apt/sources.list.d/ubuntu.sources to match stock Kubuntu Calamares output. Emptied /etc/apt/sources.list (debootstrap leftover).

/etc/apt/sources.list.d/ubuntu.sources:

Types: deb
URIs: http://archive.ubuntu.com/ubuntu
Suites: noble noble-updates noble-backports
Components: main universe restricted multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

Types: deb
URIs: http://security.ubuntu.com/ubuntu/
Suites: noble-security
Components: main universe restricted multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

Fix 5: HWE Kernel + Fallback

Problem: Debootstrap installed the GA kernel (6.8.0). Stock Kubuntu 24.04.4 ships with the HWE kernel (6.17.x).

sudo apt install linux-generic-hwe-24.04

Both kernels kept. GRUB menu:

  1. Kubuntu 24.04 (HWE 6.17) — default
  2. Kubuntu 24.04 (GA 6.8 fallback)
  3. Kubuntu 24.04 (HWE 6.17 recovery)
  4. Windows Boot Manager

Fix 6: Language Support

Problem: KDE daemon reported incomplete language support.

sudo apt install language-pack-en language-pack-kde-en language-pack-gnome-en

Fix 7: Touchscreen Rotation (SDDM Greeter)

Problem: The SDDM greeter (first-boot login screen) has no touch coordinate rotation. KDE desktop handles its own touch mapping after login, so a global fix (e.g., udev rule) causes double-rotation in the desktop.

Key insight: The fix must apply ONLY during the SDDM greeter phase. KDE takes over touch mapping once the desktop session starts.

Gotcha: xinput list --id-only "device name" silently fails in the SDDM context. Must grep the xinput list output to extract the device ID.

File changed/etc/sddm/Xsetup (shared with Fix 1):

#!/bin/sh
xrandr --output DSI-1 --rotate right

# Rotate touchscreen - only for SDDM greeter, KDE handles its own mapping
TOUCHID=$(xinput list | grep "Goodix Capacitive TouchScreen" | grep pointer | sed "s/.*id=\([0-9]*\).*/\1/")
if [ -n "$TOUCHID" ]; then
    xinput set-prop "$TOUCHID" "Coordinate Transformation Matrix" 0 1 0 -1 0 1 0 0 1
    logger "SDDM Xsetup: applied touch rotation to device $TOUCHID"
else
    logger "SDDM Xsetup: Goodix touch device not found"
fi

Matrix 0 1 0 -1 0 1 = 90° clockwise (matching xrandr --rotate right).

Fix 8: Windows Chainloader in GRUB

Problem: Selecting "Windows Boot Manager" from GRUB failed — the chainloader looked for bootmgfw.efi on the Kubuntu partition instead of the ESP.

Root cause: Missing search command to set the GRUB root to the EFI System Partition before chainloading.

Fix in /boot/grub/grub.cfg:

menuentry "Windows Boot Manager" {
    search --no-floppy --fs-uuid --set=root 24BF-0BF8
    chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}

Complete /boot/grub/grub.cfg

set default=0
set timeout=5

menuentry "Kubuntu 24.04 (HWE 6.17)" {
    search --no-floppy --fs-uuid --set=root d26b99a6-fa4b-473d-a5ee-051905693236
    linux /boot/vmlinuz-6.17.0-20-generic root=UUID=d26b99a6-fa4b-473d-a5ee-051905693236 ro quiet splash video=DSI-1:panel_orientation=right_side_up
    initrd /boot/initrd.img-6.17.0-20-generic
}

menuentry "Kubuntu 24.04 (GA 6.8 fallback)" {
    search --no-floppy --fs-uuid --set=root d26b99a6-fa4b-473d-a5ee-051905693236
    linux /boot/vmlinuz-6.8.0-106-generic root=UUID=d26b99a6-fa4b-473d-a5ee-051905693236 ro quiet splash video=DSI-1:panel_orientation=right_side_up
    initrd /boot/initrd.img-6.8.0-106-generic
}

menuentry "Kubuntu 24.04 (HWE 6.17 recovery)" {
    search --no-floppy --fs-uuid --set=root d26b99a6-fa4b-473d-a5ee-051905693236
    linux /boot/vmlinuz-6.17.0-20-generic root=UUID=d26b99a6-fa4b-473d-a5ee-051905693236 ro single
    initrd /boot/initrd.img-6.17.0-20-generic
}

menuentry "Windows Boot Manager" {
    search --no-floppy --fs-uuid --set=root 24BF-0BF8
    chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}

Known Limitations

Issue Detail Fixable?
GRUB menu sideways DSI panel in native portrait; GRUB uses EFI GOP framebuffer with no rotation support. This is also the reason behind the BIOS logo being drawn twice during POST — the firmware first displays a pre-rotated logo bitmap via the display controller, then switches to the raw GOP framebuffer and redraws line-by-line in native portrait orientation. No
Refresh rate 50Hz 50Hz is the panel default and also the Windows default. Not a Linux regression. Unknown — DSI panel timing investigation needed
Tablet mode switch Physical switch exists but kernel doesn't support PNP0C60/INT33D3 Possible — needs kernel patches

What Made This Work

SSH was the lifeline. The entire install — partitioning, filesystem creation, image transfer, bootloader setup, desktop install, every fix — was done over SSH. The safety model: Windows is always the default boot. If Linux fails, the next reboot returns to Windows and SSH is waiting.

The homelab was the workshop. Debootstrap needs a Linux environment. With WSL unavailable on IoT LTSC, the homelab node .68 served as the build machine. The root filesystem was built there, compressed, and streamed over HTTP to the MiniBook X. The 2.4GB compressed image transferred over WiFi in minutes.

Python on Windows was the bridge. Windows can't write ext4. But Python can open \\.\PhysicalDrive0 for raw I/O. A 15-line Python script replaced the need for dd, WSL, or any Linux tools on the Windows side.

Hardware Compatibility (Kubuntu 24.04, kernel 6.17 HWE)

Component Status
Display (DSI) Works (needs rotation config)
Touchscreen (Goodix) Works (needs calibration matrix for SDDM)
WiFi Works
Bluetooth Works
Audio Works
Keyboard + backlight Works
Touchpad Works
USB-C Works
Accelerometer Reads (auto-rotation needs tablet switch)
Tablet mode switch Not supported
60Hz+ refresh Stuck at 50Hz

About

Kubuntu 24.04 on Chuwi MiniBook X N100 — installed entirely over SSH from Windows, no USB boot, no installer

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages