Go back

Backing Up a Linux System as a Bootable Virtual Disk

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

image

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

image

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.

image

Then select New Partition Table.

image

Select GPT.

image

Click Create New Partition Table.

You should then see an area marked as unallocate.

image

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:

image

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.

image

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.

image

Changing Disk IDs

View NBD Information

sudo fdisk -x /dev/nbd0

image

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

image

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.

  1. First, open Virt Manager, also known as Virtual Machine Manager.

    It should look something like this:

    image

  2. Click the button in the upper-left corner to create a new virtual machine.

    Select Import existing disk image, then click Next.

    image

  3. Select Browse Local, then locate the qcow2 file you created earlier.

    image

  4. Next, select your operating system and version.

    In my case, I am using Ubuntu 22.04, so I select that option.

    image

  5. Configure the CPU and RAM allocation.

    The exact values do not matter much here; choose whatever you prefer.

    image

  6. 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.

    image

  7. On the next screen, select the following firmware:

    UEFI X86_64:/usr/share/edk2/ovmf/OVMF_CODE_4M.qcow2

    The exact option may differ on your system.

    In general, look for the keywords UEFI and OVMF_CODE_4M.

    Avoid selecting anything related to secure boot, as it may cause problems.

    image

  8. 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.

    image

  9. After confirming all settings, click Apply in the lower-right corner, then click Begin Installation in the upper-left corner.

    If everything is working correctly, the system should boot directly.

    image

Troubleshooting

During this process, you may encounter something like this:

image

Or this:

image

Or this:

image

Or this:

image

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

image

Or this, where only the cursor appears:

image

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

image

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:

  1. 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.
  2. 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:

image

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:

image

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.