Overview
The goal is not to replace Radxa's Debian base. The goal is to keep the vendor-supported boot, kernel, firmware, repositories and RK3588 integration while replacing only the KDE desktop layer and moving the finished installation to NVMe.
/config, /boot/efi and / are all on the NVMe SSD, and the root filesystem uses the full drive.At the time this procedure was validated, Radxa's download page identified the ROCK 5B Plus Debian kernel-6.1 image as rock-5b-plus_bookworm_kde_r7. Always use the current official ROCK 5B Plus image listed by Radxa.
Requirements
| Item | Purpose | Validated / recommended state |
|---|---|---|
| Radxa ROCK 5B Plus | Target RK3588 system | Procedure validated on physical ROCK 5B Plus hardware |
| Official Radxa Debian Bookworm image | Known-good board support and temporary microSD system | Kernel 6.1 Bookworm KDE image |
| microSD card | Initial boot and configuration media | Tested with a nominal 32 GB card |
| M.2 NVMe SSD | Permanent system storage | Tested with Samsung SSD 970, 500 GB class |
| Network connection | APT updates and SSH administration | Wired Ethernet is convenient during conversion |
dd command used later overwrites the entire destination drive. Identify the microSD and NVMe devices from the live machine instead of copying device names blindly from this article.1. Boot the Official Radxa Bookworm Image from microSD
Download the current ROCK 5B Plus Debian Bookworm image from Radxa's official download page. Write the extracted image to microSD with a trusted image-writing utility, insert the card, connect the display/network, and boot the board.
Once the system is running, update the image before changing the desktop:
sudo apt-get update sudo apt-get full-upgrade -y sudo reboot
2. Enable SSH
Remote administration makes the desktop conversion and disk migration substantially easier. Install and enable OpenSSH:
sudo apt update sudo apt install -y openssh-server sudo systemctl enable --now ssh sudo systemctl status ssh --no-pager
Find the board's addresses:
hostname -I
Connect from another Linux workstation:
ssh <username>@<rock-5b-plus-ip>
3. Install and Verify the NVMe SSD
Power the board down before installing the NVMe SSD. Seat the drive in the ROCK 5B Plus M.2 M-key connector, secure it mechanically, then boot again from the microSD card.
Inspect the storage topology:
lsblk -e7 -o NAME,PATH,SIZE,TYPE,FSTYPE,MOUNTPOINTS,MODEL,TRAN
The validated machine showed the running root filesystem on /dev/mmcblk1p3 and the Samsung NVMe as /dev/nvme0n1:
NAME PATH SIZE TYPE FSTYPE MOUNTPOINTS MODEL TRAN mmcblk1 /dev/mmcblk1 28.9G disk ├─mmcblk1p1 /dev/mmcblk1p1 16M part vfat /config ├─mmcblk1p2 /dev/mmcblk1p2 300M part vfat /boot/efi └─mmcblk1p3 /dev/mmcblk1p3 28.5G part ext4 / nvme0n1 /dev/nvme0n1 465.8G disk Samsung SSD 970 nvme
Confirm the active root device separately:
findmnt -no SOURCE /
Before migration, this should point to the microSD root partition.
4. Update the ROCK 5B Plus SPI Bootloader
Radxa's documented NVMe boot workflow uses its rsetup utility to update the bootloader stored in onboard SPI NOR. From the official Bookworm system, run:
sudo rsetup
1Open System Maintenance
In rsetup, select System Maintenance.
2Choose Update SPI Bootloader
Select Update SPI Bootloader.
3Select the ROCK 5B Plus target
Choose the bootloader option corresponding to the ROCK 5B Plus, follow the prompts, and allow the write to finish completely.
Reboot after the SPI update:
sudo reboot
rsetup to report completion.5. Convert Radxa KDE to the Standard Debian GNOME Desktop
Debian's task-gnome-desktop package installs the Debian desktop featuring GNOME and the applications Debian expects on that desktop. The North Rivet conversion script installs GNOME and GDM first, leaves KDE available for the first reboot, and separates KDE removal into a later verification phase.
North Rivet conversion script
Download the exact script reproduced below. It preserves the Radxa package sources and does not replace the vendor kernel or firmware.
On the ROCK 5B Plus:
chmod +x convert-radxa-bookworm-kde-to-gnome.sh ./convert-radxa-bookworm-kde-to-gnome.sh
When the script finishes:
sudo reboot
Verify the GNOME login and desktop before removing KDE. From a terminal or SSH session:
cat /etc/X11/default-display-manager systemctl status gdm3 --no-pager
The default display manager should be:
/usr/sbin/gdm3
Complete conversion script
#!/usr/bin/env bash
set -Eeuo pipefail
# North Rivet Technical Library
# Radxa Debian 12 Bookworm KDE -> Debian GNOME conversion
#
# Tested workflow target:
# Radxa ROCK 5B Plus
# Official Radxa Debian 12 Bookworm image
#
# Normal conversion:
# chmod +x convert-radxa-bookworm-kde-to-gnome.sh
# ./convert-radxa-bookworm-kde-to-gnome.sh
#
# After GNOME has been verified after a reboot:
# ./convert-radxa-bookworm-kde-to-gnome.sh --remove-kde
#
# The cleanup phase deliberately does NOT run apt autoremove. A dry-run
# autoremove report is created for review instead.
MODE="install"
usage() {
cat <<'USAGE'
North Rivet - Radxa Bookworm KDE to Debian GNOME Converter
Usage:
./convert-radxa-bookworm-kde-to-gnome.sh
./convert-radxa-bookworm-kde-to-gnome.sh --remove-kde
./convert-radxa-bookworm-kde-to-gnome.sh --help
Modes:
default Install Debian GNOME and make GDM the default display manager.
KDE is intentionally left installed until GNOME is verified.
--remove-kde Remove the high-level KDE/Plasma desktop packages after GNOME
has been verified. Does NOT run apt autoremove.
USAGE
}
case "${1:-}" in
"")
MODE="install"
;;
--remove-kde|remove-kde)
MODE="remove-kde"
;;
-h|--help)
usage
exit 0
;;
*)
printf 'ERROR: Unknown option: %s\n\n' "$1" >&2
usage >&2
exit 2
;;
esac
if [[ ${EUID} -eq 0 ]]; then
SUDO=()
else
command -v sudo >/dev/null 2>&1 || {
echo "ERROR: sudo is required when this script is not run as root." >&2
exit 1
}
SUDO=(sudo)
fi
need_cmd() {
command -v "$1" >/dev/null 2>&1 || {
printf 'ERROR: Required command not found: %s\n' "$1" >&2
exit 1
}
}
for cmd in apt-get apt-mark dpkg-query systemctl tee date grep readlink; do
need_cmd "$cmd"
done
[[ -r /etc/os-release ]] || {
echo "ERROR: /etc/os-release is not readable." >&2
exit 1
}
# shellcheck disable=SC1091
source /etc/os-release
if [[ "${ID:-}" != "debian" || "${VERSION_CODENAME:-}" != "bookworm" ]]; then
echo "ERROR: This script is intended for Debian 12 Bookworm." >&2
printf 'Detected: ID=%s VERSION_CODENAME=%s\n' \
"${ID:-unknown}" "${VERSION_CODENAME:-unknown}" >&2
exit 1
fi
MODEL="unknown"
if [[ -r /proc/device-tree/model ]]; then
MODEL="$(tr -d '\000' < /proc/device-tree/model)"
fi
STAMP="$(date +%Y%m%d-%H%M%S)"
BACKUP_DIR="/var/backups/northrivet-radxa-desktop-${STAMP}"
printf '\n============================================================\n'
printf ' North Rivet - Radxa Debian Desktop Conversion\n'
printf '============================================================\n'
printf 'OS: %s\n' "${PRETTY_NAME:-Debian 12 Bookworm}"
printf 'Board: %s\n' "${MODEL}"
printf 'Architecture: %s\n' "$(dpkg --print-architecture)"
printf 'Kernel: %s\n' "$(uname -r)"
printf 'Mode: %s\n\n' "${MODE}"
backup_state() {
echo "Creating pre-change record: ${BACKUP_DIR}"
"${SUDO[@]}" install -d -m 0755 "${BACKUP_DIR}"
dpkg-query -W -f='${binary:Package}\t${Version}\t${db:Status-Abbrev}\n' \
| "${SUDO[@]}" tee "${BACKUP_DIR}/packages-before.txt" >/dev/null
if [[ -f /etc/apt/sources.list ]]; then
"${SUDO[@]}" cp -a /etc/apt/sources.list "${BACKUP_DIR}/"
fi
if [[ -d /etc/apt/sources.list.d ]]; then
"${SUDO[@]}" cp -a /etc/apt/sources.list.d "${BACKUP_DIR}/"
fi
if [[ -f /etc/X11/default-display-manager ]]; then
"${SUDO[@]}" cp -a /etc/X11/default-display-manager "${BACKUP_DIR}/"
fi
}
install_gnome() {
backup_state
echo
echo "Updating Debian package metadata..."
"${SUDO[@]}" apt-get update
# Avoid the interactive display-manager selection dialog. GDM is the
# intended display manager for the Debian GNOME desktop.
if command -v debconf-set-selections >/dev/null 2>&1; then
printf '%s\n' \
'gdm3 shared/default-x-display-manager select gdm3' \
'sddm shared/default-x-display-manager select gdm3' \
| "${SUDO[@]}" debconf-set-selections
fi
echo
echo "Installing Debian's standard GNOME desktop task and GDM..."
"${SUDO[@]}" env DEBIAN_FRONTEND=noninteractive \
apt-get install -y task-gnome-desktop gdm3
echo
echo "Making GDM the default graphical login manager..."
if systemctl list-unit-files sddm.service >/dev/null 2>&1; then
"${SUDO[@]}" systemctl disable sddm.service >/dev/null 2>&1 || true
fi
"${SUDO[@]}" systemctl enable --force gdm3.service >/dev/null 2>&1 || true
printf '/usr/sbin/gdm3\n' \
| "${SUDO[@]}" tee /etc/X11/default-display-manager >/dev/null
"${SUDO[@]}" systemctl set-default graphical.target >/dev/null
# Keep the packages that define the desired desktop explicitly manual.
"${SUDO[@]}" apt-mark manual task-gnome-desktop gdm3 >/dev/null
echo
echo "GNOME installation is complete."
echo "KDE has NOT been removed yet; this is intentional."
echo
echo "Configured display manager:"
cat /etc/X11/default-display-manager 2>/dev/null || true
echo
echo "Next step:"
echo " sudo reboot"
echo
echo "After reboot, verify GNOME works. Only then run:"
echo " $0 --remove-kde"
}
remove_kde() {
if ! dpkg-query -W -f='${db:Status-Abbrev}' gdm3 2>/dev/null \
| grep -q '^ii'; then
echo "ERROR: gdm3 is not installed. Run the normal conversion first." >&2
exit 1
fi
if [[ "$(cat /etc/X11/default-display-manager 2>/dev/null || true)" \
!= "/usr/sbin/gdm3" ]]; then
echo "ERROR: GDM is not the configured default display manager." >&2
echo "Verify GNOME first, then rerun the cleanup." >&2
exit 1
fi
backup_state
KDE_CANDIDATES=(
task-kde-desktop
kde-standard
kde-plasma-desktop
plasma-desktop
sddm
)
INSTALLED=()
for pkg in "${KDE_CANDIDATES[@]}"; do
if dpkg-query -W -f='${db:Status-Abbrev}' "${pkg}" 2>/dev/null \
| grep -q '^ii'; then
INSTALLED+=("${pkg}")
fi
done
echo
if [[ ${#INSTALLED[@]} -eq 0 ]]; then
echo "No high-level KDE packages from the conservative cleanup list are installed."
else
echo "Removing these high-level KDE desktop packages:"
printf ' %s\n' "${INSTALLED[@]}"
echo
"${SUDO[@]}" apt-get purge -y "${INSTALLED[@]}"
fi
"${SUDO[@]}" systemctl enable --force gdm3.service >/dev/null 2>&1 || true
printf '/usr/sbin/gdm3\n' \
| "${SUDO[@]}" tee /etc/X11/default-display-manager >/dev/null
"${SUDO[@]}" apt-mark manual task-gnome-desktop gdm3 >/dev/null
REVIEW_FILE="${HOME}/debian-gnome-autoremove-review.txt"
echo
echo "Generating an autoremove DRY RUN for manual review..."
apt-get --dry-run autoremove | tee "${REVIEW_FILE}"
echo
echo "KDE high-level cleanup is complete."
echo "No automatic dependency purge was performed."
echo "Review this file before ever running apt autoremove:"
echo " ${REVIEW_FILE}"
}
case "${MODE}" in
install)
install_gnome
;;
remove-kde)
remove_kde
;;
esac
6. Clone the Finished microSD System to NVMe
This method copies the complete configured Bookworm system—including the GNOME conversion—to the NVMe. First identify the source and target again immediately before using dd:
lsblk -e7 -o NAME,PATH,SIZE,TYPE,FSTYPE,MOUNTPOINTS,MODEL,TRAN findmnt -no SOURCE /
/dev/mmcblk1 was the 28.9 GB microSD source and /dev/nvme0n1 was the 465.8 GB NVMe destination. Reversing if= and of= destroys the source installation.Make sure no NVMe partitions are mounted:
mount | grep nvme || true sudo umount /dev/nvme0n1p1 2>/dev/null || true sudo umount /dev/nvme0n1p2 2>/dev/null || true sudo umount /dev/nvme0n1p3 2>/dev/null || true
Reduce desktop activity and flush pending writes:
sudo systemctl stop gdm3 2>/dev/null || true sudo systemctl stop sddm 2>/dev/null || true sync
dd is running. For environments requiring snapshot-grade consistency, perform the clone from separate rescue media.For the validated device mapping, the copy command was:
sudo dd if=/dev/mmcblk1 of=/dev/nvme0n1 bs=16M status=progress conv=fsync sync
When dd completes, power the board completely off:
sudo shutdown -h now
Remove the microSD card before the first NVMe boot.
7. Boot Directly from NVMe
With the microSD removed, power the ROCK 5B Plus back on. The onboard SPI bootloader should locate the boot files and operating system on NVMe.
After login, prove that root is on NVMe:
findmnt -no SOURCE / findmnt /boot/efi findmnt /config lsblk
The root check should report an NVMe partition, such as:
/dev/nvme0n1p3
8. Expand the Cloned NVMe Partition and ext4 Filesystem
A raw clone initially carries the microSD's smaller partition geometry. Install growpart and GPT utilities:
sudo apt update sudo apt install -y cloud-guest-utils gdisk
Grow partition 3 to the end of the NVMe:
sudo growpart /dev/nvme0n1 3
Then expand the ext4 filesystem:
sudo resize2fs /dev/nvme0n1p3
Verify:
lsblk df -h /
If growpart reports a GPT backup-table problem
A disk cloned from smaller media can retain the backup GPT at the old end-of-disk location. Repair the GPT location, then retry:
sudo sgdisk -e /dev/nvme0n1 sudo growpart /dev/nvme0n1 3 sudo resize2fs /dev/nvme0n1p3
9. Optional: Remove the Remaining KDE Desktop Packages
After GNOME and NVMe boot have both been proven, run the conversion script's conservative cleanup mode:
./convert-radxa-bookworm-kde-to-gnome.sh --remove-kde
The cleanup removes the high-level KDE/Plasma desktop packages and SDDM, then generates an apt autoremove dry-run report:
~/debian-gnome-autoremove-review.txt
Validated Final State
The tested ROCK 5B Plus finished with all three system partitions mounted from a 500 GB-class Samsung NVMe and the ext4 root expanded across the drive.
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS mtdblock0 31:0 0 16M 0 disk zram0 253:0 0 3.9G 0 disk [SWAP] nvme0n1 259:0 0 465.8G 0 disk ├─nvme0n1p1 259:1 0 16M 0 part /config ├─nvme0n1p2 259:2 0 300M 0 part /boot/efi └─nvme0n1p3 259:3 0 465.4G 0 part / Filesystem Size Used Avail Use% Mounted on /dev/nvme0n1p3 459G 7.6G 432G 2% /
Troubleshooting
The board boots only when the microSD is installed
Boot the Radxa microSD system again, run sudo rsetup, and confirm that the SPI bootloader update for the ROCK 5B Plus completed successfully. Then shut down and retry with the SD removed.
The NVMe does not appear in lsblk
Power down, reseat the M.2 drive, and check again. For additional visibility:
sudo apt install -y nvme-cli sudo nvme list lspci
dd says the target is busy
Check mounts with findmnt or mount | grep nvme and unmount every NVMe partition before cloning. Do not unmount the microSD root filesystem that is currently running.
GDM does not become the login manager
sudo systemctl disable sddm 2>/dev/null || true sudo systemctl enable --force gdm3 printf '/usr/sbin/gdm3 ' | sudo tee /etc/X11/default-display-manager sudo systemctl set-default graphical.target sudo reboot
The cloned NVMe still shows roughly the microSD capacity
The clone worked; the partition and filesystem have simply not been expanded yet. Complete the growpart and resize2fs steps above.
References
Procedure validation date: August 30, 2026. Vendor download names and package revisions can change; verify the current ROCK 5B Plus image before beginning a new installation.