Chuwi MiniBook X N150

2025Q2.

Cast your mind back to the late 2000s and one thing you might remember is the excitement about netbooks. You sacrifice something in raw computational power, but get a lightweight, low cost and ultra-portable system. Their popularity peaked and started wane maybe 15 years ago now, but I was pleased to discover that the idea lives on in the form of the Chuwi MiniBook X N150 and have been using it as my daily driver for about a month now. Read on for some notes and thoughts on the device as well as more information than you probably want about configuring Linux on it.

The bottom line is that I enjoy it, I'd buy it again. But there are real limitations to keep in mind if you're considering following suit.

Picture of Chuwi MiniBook X N150 with a Biro for scale

Background

First a little detour. As many of my comments are made in reference to my previous laptops it's probably worth fleshing out that history a little. The first thing to understand is that my local computing needs are relatively simple and minimal. I work on large C/C++ codebases (primarily LLVM) with lengthy compile times, but I build and run tests on a remote machine. This means I only need enough local compute to comfortably navigate codebases, do whatever smaller local projects I want to do, and use any needed browser based tools like videoconferencing or GDocs.

Looking back at my previous two laptops (oldest first):

I think you can see a pattern here. As for the processors, the N5000 was part of Intel "Gemini Lake" which used the Goldmont Plus microarchitecture. This targets the same market segment as earlier Atom branded processors (as used by many of those early netbooks) but with substantially higher performance and a much more complicated microarchitecture than the early Atom (which was dual issue, in order with a 16 stage pipeline). The best reference I can see for the microarchitectures used in the N5000 and N6000 is AnandTech's Tremont microarchitecture write-up (matching the N6000), which makes copious reference to differences vs previous iterations. Both the N5000 and N6000 have a TDP of 6W and 4 cores (no hyperthreading). Notably, all these designs lack AVX support.

The successor to Tremont, was the Gracemont microarchitecture, this time featuring AVX2 and seeing much wider usage due to being used as the "E-Core" design throughout Intel's chips pairing some number of more performance-oriented P-Cores with energy efficiency optimised E-Cores. Low TDP chips featuring just E-Cores were released such as the N100 serving as a successor to the N6000 and later the N150 added as a slightly higher clocked version. There have been further iterations on the microarchitecture since Gracemont with Crestmont and Skymont, but at the time of writing I don't believe these have made it into similar E-Core only low TDP chips. I'd love to see competitive devices at similar pricepoints using AMD or Arm chips (and one day RISC-V of course), but this series of Intel chips seems to have really found a niche.

Chuwi MiniBook X N150

On to the present day:

Just looking at the specs the key trade-offs are clear. There's a big drop in battery life, but a newer faster processor and fun mini size.

Overall, it's a positive upgrade but there are definitely some downsides. Main highlights:

But of course there's a long list of niggles or drawbacks. As I say, overall it works for me, but if it didn't have these drawbacks I'd probably move more towards actively recommending it without lots of caveats:

Do beware that the laptop ships with a 12V/3A charger with a USB-C connection that apparently will use that voltage without any negotiation. It's best not to use it at all due to the risk of plugging in something that can't handle 12V input.

Conclusion: It's not perfect machine but I'm a huge fan of this form factor. I really hope we get future iterations or competing products.

Appendix A: Accessories

YMMV, but I picked up the following with the most notable clearly being the replacement SSD. Prices are the approximate amount paid including any shipping.

Appendix B: Arch Linux setup

As much for my future reference as for anything else, here are notes on installing and configuring Arch Linux on the MiniBook X to my liking, and working through as many niggles as I can. I'm grateful to Sonny Piers' GitHub repo for some pointers on dealing with initial challenges like screen rotation.

Initial install

Download an Arch Linux install image and write to a USB drive. Enter the BIOS by pressing F2 while booting and disable secure boot. I found I had to do this, then save and exit for it to stick. Then enter BIOS again on a subsequent boot and select the option to boot straight into it (under the "Save and Exit" menu).

In order to have the screen rotated correctly, we need to set the boot parameter video=DSI-1:panel_orientation=right_side_up. Do this by pressing e at the boot menu and manually adding.

Then connect to WiFi (iwctl then station wlan0 scan, station wlan0 get-networks, station wlan0 connect $NETWORK_NAME and enter the WiFi password). It's likely more convenient to do the rest of the setup via ssh, which can be done by setting a temporary root password with passwd and then connecting with ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@archiso.

Set the SSD sector size to 4k:

# Confirm 4k sector sizes are available and supported.
nvme id-ns -H /dev/nvme0n1
# Shows:
# LBA Format  0 : Metadata Size: 0   bytes - Data Size: 512 bytes - Relative Performance: 0x2 Good (in use)
# LBA Format  1 : Metadata Size: 0   bytes - Data Size: 4096 bytes - Relative Performance: 0x1 Better
nvme format --lbaf=1 /dev/nvme0n1

Now partition disks and create filesystems (with encrypted rootfs):

sfdisk /dev/nvme0n1 <<EOF
label: gpt

start=1MiB, size=511MiB, type=uefi
start=512MiB, type=linux
EOF

mkfs.fat -F32 /dev/nvme0n1p1
cryptsetup -y -v luksFormat /dev/nvme0n1p2 # enter desired unlock password
cryptsetup --perf-no_read_workqueue --perf-no_write_workqueue --persistent open /dev/nvme0n1p2 root
cryptsetup luksDump /dev/nvme0n1p2 # check flags and sector size
mkfs.xfs /dev/mapper/root

Now mount and pacstrap:

mount /dev/mapper/root /mnt
mount --mkdir /dev/nvme0n1p1 /mnt/boot
pacstrap /mnt base linux linux-firmware efibootmgr \
  xfsprogs dosfstools mdadm cryptsetup \
  python3 openssh sudo net-tools git man-db man-pages vim \
  wireless_tools iwd brightnessctl bash-completion tig \
  pkgfile powertop fzf bluez bluez-utils acpi \
  base-devel clang lld ninja cmake ncdu lua wget \
  pkgfile unzip unrar 7zip pwgen entr \
  rclone dash rsync
genfstab -U /mnt >> /mnt/etc/fstab
arch-chroot /mnt

Perform additional setup within the chroot:

sed /etc/locale.gen -i -e "s/^\#en_GB.UTF-8 UTF-8.*/en_GB.UTF-8 UTF-8/"
locale-gen
# Ignore "System has not been booted with systemd" and "Failed to connect to bus" error for next command.
systemd-firstboot --locale=en_GB.UTF-8 --timezone=Europe/London --hostname="plurp"
ln -s /dev/null /etc/udev/rules.d/80-net-setup-link.rules # disable persistent network names
sed /etc/mkinitcpio.conf -i -e 's/^HOOKS=.*/HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block encrypt filesystems fsck)/'
mkinitcpio -P

Configure EFI boot:

for BOOTNUM in $(efibootmgr | grep '^Boot0' | sed 's/^Boot\([0-9]*\).*/\1/'); do
  efibootmgr -b $BOOTNUM -B
done
efibootmgr \
  --disk /dev/nvme0n1 \
  --part 1 \
  --create \
  --label 'Arch Linux' \
  --loader /vmlinuz-linux \
  --unicode "root=/dev/mapper/root rw cryptdevice=UUID=$(blkid -s UUID -o value /dev/nvme0n1p2):root:allow-discards initrd=\initramfs-linux.img video=DSI-1:panel_orientation=right_side_up" \
  --verbose

Other setup:

mkswap --size=8G --file /swapfile
cat - <<EOF > /etc/systemd/system/swapfile.swap
[Unit]
Description=Swap file

[Swap]
What=/swapfile

[Install]
WantedBy=multi-user.target
EOF
systemctl enable swapfile.swap
printf "PasswordAuthentication no\n" > /etc/ssh/sshd_config.d/20-no-password-auth.conf
systemctl enable sshd.service
useradd -m -g users -G wheel -s /bin/bash asb
usermod --pass='!' root # disable root login
chmod +w /etc/sudoers
printf "%%wheel ALL=(ALL) ALL\n" >> /etc/sudoers
chmod -w /etc/sudoers
mkdir "/home/asb/.ssh"
export PUBLIC_SSH_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINkmWQkBZWTsPo6obkVIjQVZf7Jt2RMi1F/4hIjz4BeF asb@hoorg"
printf "%s\n" "$PUBLIC_SSH_KEY" > "/home/asb/.ssh/authorized_keys"
chmod 700 "/home/asb/.ssh"
chmod 600 "/home/asb/.ssh/authorized_keys"
chown -R "asb:users" "/home/asb/.ssh"
systemctl enable iwd.service systemd-networkd.service systemd-resolved.service systemd-timesyncd.service
cat - <<EOF > /etc/systemd/network/25-wireless.network
[Match]
Name=wlan0

[Link]
RequiredForOnline=routable

[Network]
DHCP=yes
IgnoreCarrierLoss=3s
EOF
pkgfile --update
cat - <<EOF >> /etc/bash.bashrc
if [[ -r /usr/share/doc/pkgfile/command-not-found.bash ]]; then
  . /usr/share/doc/pkgfile/command-not-found.bash
fi
EOF

Set user password:

passwd asb # and enter password

ctrl-d once to exit the sysroot and:

ln -sf ../run/systemd/resolve/stub-resolv.conf /mnt/etc/resolv.conf

You can now reboot into the fresh Arch install.

Graphical environment setup after reboot

Install various additional packages:

sudo pacman -S xorg-server arandr firefox openbox xfce4-terminal pavucontrol \
  openbox pcmanfm xorg-xinit i3lock dmenu chromium cheese mpv mpg123 \
  vlc xorg-fonts-misc xorg-mkfontscale ttf-dejavu xdotool mesa-utils gvim \
  firefox-ublock-origin intel-media-driver libva-utils rclone file-roller \
  xclip discount python-requests wmctrl foliate llvm sox eog gimp inkscape audacity \
  pipewire wireplumber pipewire-pulse wmname polkit evince xorg-xev scrot \
  alsa-utils alsa-tools aichat xorg-xinput xsettingsd python-bleak \
  xorg-xrefresh kdeconnect

When launching X the screen is rotated. This can be fixed temporarily with xrandr -o right. But to fix it properly:

sudo tee /etc/X11/xorg.conf.d/20-defaultrotatescreen.conf > /dev/null << 'EOF'
Section "Monitor"
  Identifier "DSI-1"
  Option     "rotate" "right"
EndSection
EOF

The touchscreen input also needs to be rotated to work properly. See here for guidance on the transformation matrix for xinput and confirm the name to match with xinput list.

sudo tee /etc/X11/xorg.conf.d/20-defaultrotatetouchscreen.conf > /dev/null << 'EOF'
Section "InputClass"
  Identifier   "GoodixTouchscreen"
  MatchProduct "Goodix Capacitive TouchScreen"
  Option       "TransformationMatrix" "0 1 0 -1 0 1 0 0 1"
EndSection
EOF

Install some additional aur packages:

git clone https://aur.archlinux.org/yay.git && cd yay
makepkg -si
cd .. && rm -rf yay
yay xautolock
yay ps_mem

Use UK keymap and in X11 use caps lock as escape:

localectl set-keymap uk
localectl set-x11-keymap gb "" "" caps:escape

The device has a US keyboard layout which has one less key than than the UK layout and several keys in different places. As I regularly use a UK layout external keyboard, rather than just get used to this I set a UK layout and use AltGr keycodes for backslash (AltGr+-) and pipe (AltGR+`).

For audio support, I didn't need to do anything other than get rid of excessive microphone noise by opening alsamixer and turning "Interl Mic Boost" down to zero.

Suspend rather than shutdown when pressing power button

It's too easy to accidentally hit the power button especially when plugging/unplugging usb-c devices, so lets make it just suspend rather than shutdown.

sudo mkdir -p /etc/systemd/logind.conf.d
sudo tee /etc/systemd/logind.conf.d/power-button.conf > /dev/null << 'EOF'
[Login]
HandlePowerKey=suspend
EOF

Then systemd reload systemd-logind.

Enabling deep sleep

See the Arch wiki for a discussion. s2idle and deep are reported as supported from /sys/power/mem_sleep, but the discharge rate leaving the laptop suspended overnight feels higher than I'd like. Let's enable deep sleep in the hope it reduces it.

sudo mkdir -p /etc/systemd/sleep.conf.d
sudo tee /etc/systemd/sleep.conf.d/deep-sleep.conf > /dev/null << 'EOF'
[Sleep]
MemorySleepMode=deep
EOF

Check last sleep mode used with sudo journalctl | grep "PM: suspend" | tail -2. And check the current sleep mode with cat /sys/power/mem_sleep. Checking the latter after boot you're likely to be worried to see that s2idle is still default. But try suspending and then checking the journal and you'll see systemd switches it just prior to suspending. (i.e. the setting works as expected, even if it's only applied lazily).

I haven't done a reasonably controlled test of the impact.

Changing DPI

The strategy is to use xsettingsd to update applications on the fly that support it, and otherwuse update Xft.dpi in Xresources. I've found a DPI of 120 works well for me. So add systemctl --user restart xsettingsd to .xinitrc as well as a call to this set_dpi script with the desired DPI:

!/bin/sh

DPI="$1"

if [ -z "$DPI" ]; then
  echo "Usage: $0 <dpi>"
  exit 1
fi

CONFIG_FILE="$HOME/.config/xsettingsd/xsettingsd.conf"
mkdir -p "$(dirname "$CONFIG_FILE")"
if ! [ -e "$CONFIG_FILE" ]; then
  touch "$CONFIG_FILE"
fi

if grep -q 'Xft/DPI' "$CONFIG_FILE"; then
  sed -i "s|Xft/DPI.*|Xft/DPI $(($DPI * 1024))|" "$CONFIG_FILE"
else
  echo "Xft/DPI $(($DPI * 1024))" >> "$CONFIG_FILE"
fi

systemctl --user restart xsettingsd.service

echo "Xft.dpi: $DPI" | xrdb -merge

echo "DPI set to $DPI"

If attaching to an external display where a different DPI is desirable, just call set_dpi as needed.

Enabing Jabra bluetooth headset

Configuring Logitech Marble Mouse

sudo tee /etc/X11/xorg.conf.d/10-libinput.conf > /dev/null << 'EOF'
Section "InputClass"
  Identifier   "Marble Mouse"
  MatchProduct "Logitech USB Trackball"
  Driver       "libinput"
  Option       "ScrollMethod"    "button"
  Option       "ScrollButton"    "8"
  Option       "MiddleEmulation" "on"
EndSection
EOF

Automatically enabling/disabling display outputs upon plugging in a monitor

The srandrd tool provides a handy way of listening for changes in the plug/unplugged status of connections and launching a shell script. First try it out with the following to observe events:

yay srandrd
cat - <<'EOF' > /tmp/echo.sh
echo $SRANDRD_OUTPUT $SRANDRD_EVENT $SRANDRD_EDID
EOF
chmod +x /tmp/echo.sh
srandrd -n /tmp/echo.sh
# You should now see the events as you plug/unplug devices.

So this is simple - we just write a shell script that srandrd will invoke which calls xrandr as desired when connect/disconnect of the device with the target EDID happens? Almost. There are two problems I need to work around:

  1. The monitor I use for work is fairly bad at picking up a 4k60Hz input signal. As far as I can tell this is independent of the cable used or input device. What does seem to reliably work is to output a 1080p signal, wait a bit, and then reconfigure to 4k60Hz.
  2. The USB-C cable I normally plug into in my sitting room is also connected to the TV via HDMI (I often use this for my Steam Deck). I noticed occasional graphical slowdowns and after more debugging found I could reliably see this in hiccups / reduced measured frame rate in glxgears that correspond with recurrent plug/unplug events. The issue disappears completely if video output via the cable is configured once and then unconfigured again. Very weird, but at least there's a way round it.

Solving both of the above can readily be addressed by producing a short sequence of xrandr calls rather than just one. Except these xrandr calls themselves trigger new events that cause srandrd to reinvoke the script. So I add a mechanism to have the script ignore events if received in short succession. We end up with the following:

#!/usr/bin/sh

EVENT_STAMP=/tmp/display-change-stamp

# Recognised displays (as reported by $SRANDRD_EDID).
WORK_MONITOR="720405518350B628"
TELEVISION="6D1E82C501010101"

msg() {
  printf "display-change-handler: %s\n" "$*" >&2
}

# Call xrandr, but refresh $EVENT_STAMP just before doing so. This causes
# connect/disconnect events generated by the xrandr operation to be skipped at
# the head of this script. Call xrefresh afterwards to ensure windows are
# redrawn if necessary.
wrapped_xrandr() {
  touch $EVENT_STAMP
  xrandr "$@"
  xrefresh
}

msg "received event '$SRANDRD_OUTPUT: $SRANDRD_EVENT $SRANDRD_EDID'"

# Suppress event if within 2 seconds of the timestamp file being updated.
if [ -f $EVENT_STAMP ]; then
  cur_time=$(date +%s)
  file_time=$(stat -c %Y $EVENT_STAMP)
  if [ $(( cur_time - file_time)) -le 2 ]; then
    msg "suppressing event (exiting)"
    exit 0
  fi
fi
touch $EVENT_STAMP

is_output_outputting() {
  xrandr --query | grep -q "^$1 connected.*[0-9]\+x[0-9]\++[0-9]\++[0-9]\+"
}

# When connecting the main 'docked' display, disable the internal screen. Undo
# this when disconnecting.
case "$SRANDRD_EVENT $SRANDRD_EDID" in
  "connected $WORK_MONITOR")
    msg "enabling 1920x1080 output on $SRANDRD_OUTPUT, disabling laptop display, and sleeping for 10 seconds"
    wrapped_xrandr --output DSI-1 --off --output $SRANDRD_OUTPUT --mode 1920x1080
    sleep 10
    msg "switching up to 4k output"
    wrapped_xrandr --output DSI-1 --off --output $SRANDRD_OUTPUT --preferred
    msg "done"
    exit
    ;;
  "disconnected $WORK_MONITOR")
    msg "re-enabling laptop display and disabling $SRANDRD_OUTPUT"
    wrapped_xrandr --output DSI-1 --preferred --rotate right --output $SRANDRD_OUTPUT --off
    msg "done"
    exit
    ;;
  "connected $TELEVISION")
    # If we get the 'connected' event and a resolution is already configured
    # and being emitted, then do nothing as the event was likely generated by
    # a manual xrandr call from outside this script.
    if is_output_outputting $SRANDRD_OUTPUT; then
      msg "doing nothing as manual reconfiguration suspected"
      exit 0
    fi
    msg "enabling then disabling output $SRANDRD_OUTPUT which seems to avoid subsequent disconnect/reconnects"
    wrapped_xrandr --output $SRANDRD_OUTPUT --mode 1920x1080
    sleep 1
    wrapped_xrandr --output $SRANDRD_OUTPUT --off
    msg "done"
    exit
    ;;
  *)
    msg "no handler for $SRANDRD_EVENT $SRANDRD_EDID"
    exit
    ;;
esac

Outputting to in-built screen at 60Hz (not yet solved)

The screen is unfortunately limited to 50Hz out of the box, but at least on Windows it's possible to use Custom Resolution Utility to edit the EDID and add a 1200x1920 60Hz mode (reminder: the display is rotated to the right which is why width x height is the opposite order to normal). To add Custom Resolution utility:

As is often the case, the Arch Linux wiki has some relevant guidance on configuring an EDID override on Linux. I tried to follow the guidance by:

So this remains unresolved for the time being.

Avoiding screen tearing (not yet solved)

Screen tearing is quite noticeable when scrolling some sites. I'd hoped that enabling a compositor like picom would address this, but no combination of options seemed to do the job. Looking at the issue tracker, there are known problems with tearing in a rotated display that should be solved by using a version of the Xorg modesetting driver supporting TearFree. As Xorg hasn't had a new release in forever this requires the xorg-server-git AUR package. Unfortunately, after building this I found dwm hangs at launch and haven't put aside the time to investigate further.


Article changelog