This guide introduces a method for backing up a Linux system.
The goal of this guide is to create a virtual disk that can be booted directly by a virtual machine.
Please note that the resulting disk is intended to be booted directly in a virtual machine. Booting it directly on physical hardware may not work or may require additional configuration.
Prerequisites and Notes
First, you will need an additional disk.
One prerequisite of this method is that the source disk must not be mounted.
In other words, you will need at least:
- The source disk
- A LiveUSB to use as the operating system
- Another disk, either external or installed inside the computer
Next, you will generally need to boot from the LiveUSB.
After booting, make sure the source disk is not mounted.
The additional disk should be formatted beforehand, for example using Brtfs or another suitable filesystem.
Once these steps are complete, continue with the instructions below.
Installing Dependencies
First, make sure QEMU is installed.
If it is not installed, use the following command:
sudo dnf install qemu
Virtual Disk Mounting
Create a QCOW2 Image (Its Size Must Be at Least as Large as the Source Disk)
Use the following command to create a virtual disk:
qemu-img create -f qcow2 test.qcow2 120G

The -f qcow2 option specifies QCOW2 as the format, which supports snapshots and dynamically growing storage.
test.qcow2 is the path and filename of the virtual disk you want to create.
1G represents the virtual size of the disk.
Change this value as needed.
Also note that the virtual disk size must be at least as large as the source disk.
Load the NBD Module
The NBD module is somewhat like providing a mount point for a virtual disk.
We need to load this module first.
Use the following command to load the Linux kernel NBD module:
sudo modprobe nbd max_part=8
Please note that if you configure max_part, do not set it to anything lower than 4, or problems may occur.
Attach the Virtual Disk
Use the following command to attach the virtual disk to nbd:
sudo qemu-nbd -c /dev/nbd0 test.qcow2
Set /dev/nbd0 to the NBD device you actually want to use.
For example, /dev/nbd1 would also work.
This guide uses /dev/nbd0.
test.qcow2 is the path and filename of the virtual disk created earlier.
After attaching it, /dev/nbd0 will appear to the system like a newly connected physical disk.
You can see it using lsblk.
Verify That the Disk Was Attached Successfully
lsblk | grep nbd

You should see the newly attached nbd0 device along with the space allocated earlier.
In this example, the size is 10G.
Partition Table and Partition Operations
Launch KDE Partition Manager
First, select the nbd0 device created earlier.

Then select New Partition Table.

Select GPT.

Click Create New Partition Table.
You should then see an area marked as unallocate.

This means that the disk has not yet been formatted.
That is fine.
Now go to the disk you want to back up.
Note: The disk being backed up must not be mounted at this point. In other words, the source disk must remain unmounted.
The source disk may look something like this, using a system disk as an example:

Normally, you might expect that we would need to format the new disk before continuing.
However, in this case, we can directly copy entire partitions onto it instead.
You can click directly on a partition block, which is the colored area shown above.
Then use Crtl+C and Crtl+V to copy it to the new nbd disk.
Since this is only a demonstration, I will simply create three arbitrary partitions here.

After everything has been configured, remember to click Apply in the upper-left corner.
Otherwise, the system will not actually perform any of the operations.

Changing Disk IDs
View NBD Information
sudo fdisk -x /dev/nbd0

We can see several important pieces of information:
Disk identifier: 25BDF3F9-D9D2-4F8A-9A2C-F282E6BE1BA3
Device Start End Sectors Type-UUID UUID Name Attrs
/dev/nbd0p1 2048 4196351 4194304 0FC63DAF-8483-4772-8E79-3D69D8477DE4 C02A8C28-DCD6-4E90-8BC7-4443ED813166
/dev/nbd0p2 4196352 8390655 4194304 0FC63DAF-8483-4772-8E79-3D69D8477DE4 E977A3D4-3AF0-4B6E-A074-23B760793746
/dev/nbd0p3 8390656 12584959 4194304 0FC63DAF-8483-4772-8E79-3D69D8477DE4 8BBC01F3-AB90-4153-9227-5C47A57453F0
The important values here are the Disk identifier, as well as the Type-UUID and UUID of each partition.
We will use these later.
View Information About the Disk Being Backed Up
fdisk -x /dev/nvme0n1

Again, we can see several important pieces of information:
Disk identifier: D356EF25-CA85-4E80-88CC-19046A20857C
Device Start End Sectors Type-UUID UUID Name Attrs
/dev/nvme0n1p1 2048 4095 2048 21686148-6449-6E6F-744E-656564454649 4DD9ED3C-EB33-4233-B03E-BD6B5E3E6DF7
/dev/nvme0n1p2 4096 2101247 2097152 BC13C2FF-59E6-4262-A352-B275FD6F7172 5104E937-01D8-4357-97AB-660AA3E4F456
/dev/nvme0n1p3 2101248 209713151 207611904 0FC63DAF-8483-4772-8E79-3D69D8477DE4 D3336CA9-5AB2-4638-B3A9-1CA94E708A9C
Again, pay attention to the Disk identifier, as well as the Type-UUID and UUID of each partition.
We will use these values later.
Modify the Disk Identifier
Use the following command to enter the fdisk utility:
sudo fdisk /dev/nbd0
You should see something similar to:
~/test_virtual_disk
sudo fdisk /dev/nbd0
Welcome to fdisk (util-linux 2.40.4).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help):
Enter x to enter Expert mode and access advanced settings:
Command (m for help): x
Expert command (m for help):
Enter i to modify the identifier:
Expert command (m for help): i
Enter new disk UUID (in 8-4-4-4-12 format):
After entering i, enter the disk UUID.
Note that the UUID used here should be the Disk identifier obtained earlier from /dev/nvme0n1.
In other words, our goal is to change the virtual disk’s Disk identifier so that it matches the Disk identifier of the physical disk being backed up.
Enter the Disk identifier of the source disk.
In this example, that is the value from /dev/nvme0n1:
Enter new disk UUID (in 8-4-4-4-12 format): D356EF25-CA85-4E80-88CC-19046A20857C
Disk identifier changed from 25BDF3F9-D9D2-4F8A-9A2C-F282E6BE1BA3 to D356EF25-CA85-4E80-88CC-19046A20857C.
Expert command (m for help):
Enter r, then enter w to write the changes.
Before you write the changes, all operations remain temporary.
Modify the UUID of Each Partition
Use the following command to enter the fdisk utility:
sudo fdisk /dev/nbd0
~/test_virtual_disk
sudo fdisk /dev/nbd0
Welcome to fdisk (util-linux 2.40.4).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help):
Enter x.
Then enter u.
Select the partition:
Command (m for help): x
Expert command (m for help): u
Partition number (1-3, default 3): 1
Enter the new UUID:
Partition number (1-3, default 3): 1
New UUID (in 8-4-4-4-12 format): 21686148-6449-6E6F-744E-656564454649
Partition UUID changed from C02A8C28-DCD6-4E90-8BC7-4443ED813166 to 21686148-6449-6E6F-744E-656564454649.
Expert command (m for help):
Enter r, then enter w to write the changes.
Before writing the changes, all operations remain temporary.
Modify the TYPE-UUID of Each Partition
Use the following command to enter the fdisk utility:
sudo fdisk /dev/nbd0
~/test_virtual_disk
sudo fdisk /dev/nbd0
Welcome to fdisk (util-linux 2.40.4).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help):
Enter m.
Then enter t.
GPT
M enter protective/hybrid MBR
Generic
d delete a partition
F list free unpartitioned space
l list known partition types
n add a new partition
p print the partition table
t change a partition type
v verify the partition table
i print information about a partition
e resize a partition
Misc
m print this menu
x extra functionality (experts only)
Script
I load disk layout from sfdisk script file
O dump disk layout to sfdisk script file
Save & Exit
w write table to disk and exit
q quit without saving changes
Create a new label
g create a new empty GPT partition table
G create a new empty SGI (IRIX) partition table
o create a new empty MBR (DOS) partition table
s create a new empty Sun partition table
Command (m for help): t
Partition number (1-3, default 3):
Select the partition:
Command (m for help): t
Partition number (1-3, default 3): 1
Partition type or alias (type L to list all):
Enter the new type UUID:
Command (m for help): t
Partition number (1-3, default 3): 1
Partition type or alias (type L to list all): 21686148-6449-6E6F-744E-656564454649
Changed type of partition 'Linux filesystem' to 'BIOS boot'.
Command (m for help):
Enter r, then enter w to write the changes.
Before writing the changes, all operations remain temporary.
Detach the Disk
qemu-nbd -d /dev/nbd0
The backup is now complete.
Verify That the Backup Is Correct
Normal Boot Scenario
Next, we need to verify that the backup works correctly.
We will use a virtual machine to test it.
-
First, open
Virt Manager, also known asVirtual Machine Manager.It should look something like this:

-
Click the button in the upper-left corner to create a new virtual machine.
Select
Import existing disk image, then clickNext.
-
Select
Browse Local, then locate theqcow2file you created earlier.
-
Next, select your operating system and version.
In my case, I am using
Ubuntu 22.04, so I select that option.
-
Configure the CPU and RAM allocation.
The exact values do not matter much here; choose whatever you prefer.

-
Next, make sure to enable
Customize configuration before install.If you forget to select this option, you will need to start the previous steps again, so this is important.

-
On the next screen, select the following firmware:
UEFI X86_64:/usr/share/edk2/ovmf/OVMF_CODE_4M.qcow2The exact option may differ on your system.
In general, look for the keywords
UEFIandOVMF_CODE_4M.Avoid selecting anything related to
secure boot, as it may cause problems.
-
This setting is somewhat important.
The default display device should normally be
QXL.Leave it unchanged for now. If you encounter problems later, change it to
VGA.
-
After confirming all settings, click
Applyin the lower-right corner, then clickBegin Installationin the upper-left corner.If everything is working correctly, the system should boot directly.

Troubleshooting
During this process, you may encounter something like this:

Or this:

Or this:

Or this:

You may also encounter this annoying situation where the system simply freezes:

Or this, where only the cursor appears:

And finally, you may even end up with a completely black screen without even a cursor:

There are many other possible variations, so I will not list all of them here.
If you encounter one of these situations, you basically have two options:
- Ignore it. Since the partitions have already been copied to the virtual disk and the disk shows that storage space is being used, the backup is probably there. You can assume the backup itself worked and that the problem may be related to the system data.
- Actually check what is going wrong.
If you choose option 2, I happen to have a possible solution.
It may not solve your particular problem, but it solved mine.
First, repeatedly press Esc while the virtual machine is booting.
You should enter GRUB, which should look something like this:

Next, run:
ls
We first need to see what the disk layout looks like.
You may get output similar to:
grub> ls
(proc) (memdisk) (hd0) (hd0,gpt2) (hd0,gpt1)
Next, you need to determine which partition contains your system.
Run ls against each partition.
If you have several partitions, repeat this for each one.
In my case, I only have two, so I check both.
Example output:
grub> ls (hd0,gpt2)/
error: unknown filesystem.
grub> ls (hd0,gpt2)
Partition hd0,gpt2: No known filesystem detected - Partition start at 525312KiB - Total size 976236544KiB
grub> _
We can see that gpt2 is significantly larger, so the bootable system partition should be located there.
Run:
ls (hd0,gpt2)/boot/
This checks whether the partition contains a boot directory and shows its contents.
The result may look like this:
grub> ls (hd0,gpt2)/boot/
efi/ grub/ config-6.8.0-60-generic vmlinuz-6.8.0-60-generic initrd.img initrd.img.old memtest86+.bin memtest86+.elf memtest86+_multiboot.bin vmlinuz
vmlinuz.old config-6.8.0-87-generic config-6.8.0-90-generic vmlinuz-6.8.0-87-generic System.map-6.8.0-87-generic System.map-6.8.0-90-generic
System.map-6.8.0-60-generic initrd.img-6.8.0-87-generic vmlinuz-6.8.0-90-generic initrd.img-6.8.0-60-generic initrd.img-6.8.0-90-generic
Here, we can see several kernel versions.
We will use the newest one:
vmlinuz-6.8.0-90-generic
The corresponding image is:
initrd.img-6.8.0-90-generic
At this point, we have confirmed that the kernel does exist.
Next, run the following commands:
set root=(hd0,gpt2)
linux /boot/vmlinuz-6.8.0-90-generic root=/dev/vda2 ro quiet splash nomodeset
initrd /boot/initrd.img-6.8.0-90-generic
boot
There are a few important things to note:
/boot/vmlinuz-6.8.0-90-generic should be replaced with the newest kernel you found on your own system.
/dev/vda2 should be replaced with your own partition.
/boot/initrd.img-6.8.0-90-generic should also be replaced with the corresponding image on your system.
In other words, all commands shown here are examples and must be adjusted for your own environment.
A more detailed explanation follows.
set root=(hd0,gpt2)
# Tell GRUB where the root of the filesystem is
# hd0 = the first disk
# gpt2 = the second partition in the GPT partition table (the ext4 system partition)
# This tells the system where to look for the paths used in the following commands
linux /boot/vmlinuz-6.8.0-90-generic root=/dev/vda2 ro quiet splash nomodeset
# linux = GRUB command used to load the Linux kernel
# /boot/vmlinuz-6.8.0-90-generic = path to the kernel image
# root=/dev/vda2 = tells the kernel where the root filesystem is located after booting
# (the disk is exposed as vda because it uses VirtIO)
# ro = initially mount the root filesystem as read-only, which is part of the normal boot process
# quiet splash = quiet mode + graphical boot screen
# nomodeset = disable GPU KMS and use a basic display driver
initrd /boot/initrd.img-6.8.0-90-generic
# Load the initial ramdisk
# This is a temporary filesystem containing drivers and tools required during the early boot process
# It must match the kernel version
boot
# Start the boot process and hand control over to the kernel
In theory, after entering all of these commands, the system should boot successfully.
After the system starts, it should look something like this:

Once inside the system, there are still a few things you should do.
First, open a terminal and run:
sudo nano /etc/default/grub
We need to modify some boot-related settings.
Change:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
to:
GRUB_CMDLINE_LINUX_DEFAULT=""
Press Crtl+O to save, then Crtl+X to exit.
Next, run:
sudo update-grub
After confirming that GRUB has been updated successfully, shut down the virtual machine.
Then return to step 8. in the Normal Boot Scenario section above.
Change the display device to VGA.
The system should then be able to boot normally.
Possible Cause of the Problem
I am not completely sure what the actual cause of the problem is.
However, it appears to be related to display handling, particularly these three boot parameters:
quiet splash nomodeset
These parameters mean the following:
quiet
- Reduces the number of kernel messages shown during boot
- Displays only errors and important messages
splash
- Enables the graphical boot animation
- Displays a graphical boot screen, such as the Ubuntu logo
- Removing it causes boot messages to be displayed as text instead of showing the logo
nomodeset
- Disables Kernel Mode Setting (KMS)
- Prevents the kernel from loading the GPU’s native driver
- Forces the system to use a basic VESA/VGA framebuffer
- Results in a lower resolution, but provides the highest compatibility
In my own testing, I found that the system boots normally as long as quiet and splash are removed and the display device is changed to VGA.
If quiet and splash are removed but QXL is still used, the system still boots to a black screen.
Based on this, I suspect the issue is related to the display protocol or virtual display configuration.
I spent quite a long time investigating this, so I decided to stop here for now.
This is the solution that currently works for me.