1. Overview

In this tutorial, we’ll explore how to migrate a Linux operating system from a virtual environment to a physical system. This provides direct access to physical hardware with improved processing power, graphics performance, and overall system responsiveness.

As a result, applications and workloads that are resource-intensive and may not perform optimally in a virtualized environment can perform better. In addition, we can take full advantage of hardware features and peripherals that may not be fully supported or accessible in a virtual environment.

Although we’ll cover migrating using VirtualBox, the concepts and procedures apply to a wide range of virtualization solutions. This is because VirtualBox supports disk image files from other popular hypervisors.

2. Our VirtualBox Test Machine

OSBoxes is a resource for ready-to-use virtual machine images for VirtualBox and VMware. As an example, let’s download a VirtualBox image of Debian, whose .7z file is about 1.1GB. To get the best download speed, we can parallelize the download using aria2. Its -s 4 -x 4 options split the file into four segments and download each segment simultaneously, greatly speeding up the overall download:

$ aria2c -c -s 4 -x 4 -o "Debian.7z" \
'https://sourceforge.net/projects/osboxes/files/v/vb/14-D-b/12.4.0/64bit.7z/download'

As a security measure, let’s compare the sha256sum hash provided by OSBoxes with that of the downloaded file:

$ echo "57c6f93f1250ae7e1538358a838b682a95bf97fb2d36160b4d8c8354e9d8a24f \
Debian.7z" | sha256sum -c
Debian.7z: OK

Finally, let’s use 7-Zip to extract the VirtualBox .vdi virtual disk and check its size:

$ 7z x Debian.7z
[...]
$ cd 64bit
$ ls -lh | grep *.vdi
-rw-rw-r-- 1 francesco francesco 8,0G Jan 22 04:14 Debian 12.4.0 (64bit).vdi
$ file "Debian 12.4.0 (64bit).vdi" 
Debian 12.4.0 (64bit).vdi: VirtualBox Disk Image, [...] 536870912000 bytes

Let’s convert bytes to GB for ease of comparison: 536870912000/(1024^3)=500GB.

The difference between the 8GB shown by ls and the 500GB shown by file indicates that this virtual disk is dynamically allocated. That is, it’s a 500GB virtual disk, but its .vdi file uses only the physical space needed to store the data it contains.

3. From Virtual Machine to Physical System

Converting a virtual machine to a physical one essentially involves using dd to copy the contents of its virtual disk in RAW format to a physical disk, and then booting a physical computer from that disk. It’s important to note that this process will erase the original contents of the target physical disk.

To avoid overwriting our computer’s internal disk, we’ll use a 16GB USB flash drive as the target disk. However, it’s possible to use any physical disk, internal or external, as long as it has enough capacity for the virtual machine’s data.

In addition, if we’re using our own virtual machine instead of the sample .vdi file, we should make sure that all snapshots have been removed to align the state of the machine with the state of the virtual disk. Also, since we’re going to be modifying partitions, it’s handy to have a backup.

Last, but not least, let’s download the ISO image of GParted Live, as we’ll need it for the next steps.

3.1. Prepare the RAW Image

First, we’ll check the exact size of our target disk. Let’s remember to replace /dev/sdx with the actual device identifier for our target disk:

$ sudo blockdev --getsize64 /dev/sdx
15634300928

Here, the –getsize64 option is crucial. It tells blockdev to get the total size of the device in bytes. It’s the total usable space on the device, which is exactly what we need for tasks like copying a RAW image with dd. The space is equivalent to 14910 MB, so let’s create a RAW image of this size:

$ dd if=/dev/zero of=diskimage.raw bs=1M count=14910

Now, let’s set up our raw disk image as a loop device. The -f option in losetup finds the first available loop device and attaches the disk image to it, while the -a option, combined with grep, helps identify the loop device associated with our disk image by filtering the list of all loop devices.

$ sudo losetup -f ./diskimage.raw
$ losetup -a | grep diskimage.raw
/dev/loop0: []: (/home/francesco/raw/64bit/diskimage.raw)

Let’s use VBoxManage to create a VirtualBox .vmdk disk file that references this loop device directly. This enables us to use the RAW disk image inside a VirtualBox VM:

$ sudo VBoxManage createmedium disk --filename=./diskimage.vmdk \
--format=VMDK --variant RawDisk --property RawDrive=/dev/loop0
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Medium created. UUID: f9b816d3-a518-493b-9b04-44fa72a460bf

We need to run VirtualBox as root to access the newly configured disk:

$ sudo VirtualBox

In VirtualBox’s Virtual Media Manager, we’ll find the new diskimage.vmdk RAW disk already added by the previous VBoxManage command:

VirtualBox Media ManagerThe next step is to copy the data from Debian 12.4.0 (64bit).vdi to diskimage.vmdk.

3.2. Clone the .vdi Disk to the RAW Image

Let’s focus on the next steps in the VirtualBox GUI:

  1. Add the .vdi file to the Virtual Media Manager.
  2. Add the previously downloaded GParted ISO to the Virtual Media Manager.
  3. Create a virtual machine with Debian 12.4.0 (64bit).vdi, diskimage.vmdk, and the GParted ISO attached.
  4. Set the GParted ISO as the boot device.
  5. Start the virtual machine and open GParted.

We can see all these steps in the video:

There are several strategies and tools for cloning /dev/sda, the 500GB .vdi file, to /dev/sdb, the 16GB RAW image.

Let’s take a simple route:

  1. Remove the swap partition to save space.
  2. Resize and move the other partitions so that they take up less space than the total space of /dev/sdb.
  3. Perform the cloning with dd if=/dev/sda of=/dev/sdb bs=1M.
  4. Check the cloned partitions and possibly enlarge the last one to use all the available space.

These steps take several minutes to complete, so we edited the video, leaving only the meaningful parts:

Our RAW image is ready, but before we go any further, let’s make sure it’s actually bootable.

3.3. Test if the RAW Image Is Bootable

To make sure the RAW image is ready to use, let’s leave it as the only disk connected to VirtualBox and boot from it. The pre-installed user on our test distribution has osboxes as username and osboxes.org as password:

In this case, everything runs well except for a noticeable slowness during the boot process. Therefore, for convenience, we edited the video to speed up the process.

The slowness of the boot process could be due to several factors, such as the overhead of accessing the RAW image through a loop device and a .vmdk file, which adds an extra layer of virtualization and potentially increases I/O latency.

In addition, the lack of optimizations typically present in the .vdi format, such as caching and efficient snapshot management, can contribute to reduced disk performance.

On the other hand, if there are boot problems, we should check the /etc/fstab file and GRUB:

  • /etc/fstab is a configuration file that dictates how and where the system’s storage devices and partitions are mounted, detailing each with specific mount points, file system types, and options.
  • GRUB manages the boot process, allowing the selection of operating systems and kernel versions.

Without going into details, Boot-Repair-Disk is a rescue live CD that can help fix GRUB problems automatically. Also, the easiest way to fix problems in /etc/fstab is to refer to partitions by UUIDs instead of device names.

3.4. Copy RAW to Physical Disk

From here on, we don’t need VirtualBox anymore, so let’s remove the previously created virtual machine and the .vmdk and .vdi files from the VirtualBox Media Manager. Then, let’s delete the .vmdk file and detach the RAW image from the loop device:

$ rm diskimage.vmdk 
$ sudo losetup -d /dev/loop0

Now, we’re ready to copy the RAW image to the USB flash drive. So, let’s create a simple Bash script, named copy.sh, that gives us clear feedback:

$ cat copy.sh
#!/bin/bash

# Define variables
USB_DEVICE="/dev/sdx" # Replace /dev/sdx with the actual device identifier
RAW_IMAGE="diskimage.raw"

# Get the size of the RAW image and the USB device
rawSize=$(ls -l $RAW_IMAGE | awk '{print $5}')
usbSize=$(sudo blockdev --getsize64 $USB_DEVICE)

# Compare the sizes to ensure the RAW image will fit on the USB device
if [ $rawSize -le $usbSize ]; then
    echo "The RAW image size is within the capacity of the USB flash drive."
    # Copy the RAW image to the USB device
    sudo dd if=$RAW_IMAGE of=$USB_DEVICE bs=4M status=progress && sync
    if [ $? -eq 0 ]; then
        echo "Copy completed successfully."
    else
        echo "Copy failed."
    fi
else
    echo "The RAW image size exceeds the capacity of the USB flash drive. Please check the image file or use a larger USB drive."
    exit 1
fi

Finally, we grant the script execute permissions and run it:

$ chmod u+x copy.sh
$ ./copy.sh 
[sudo] password for francesco:           
The RAW image size is within the capacity of the USB flash drive.
[...]
15634268160 bytes (16 GB, 15 GiB) copied, 1651,28 s, 9,5 MB/s
Copy completed successfully.

The migration is complete. All that remains is to start the system.

3.5. Boot the Migrated OS

In most cases, we can simply press a special key when we turn on our computer to select the boot device.

Sometimes, however, choosing a boot device isn’t as simple and intuitive, especially when we have Windows installed on our physical machine. In this case, we need to look for information relevant to the version of Windows that we’re using.

We should remember that if the system boots but doesn’t complete the boot process, it doesn’t mean we did anything wrong. In this case, the migrated machine can have settings incompatible with our hardware or may lack certain drivers or use a kernel for a different architecture. If we’re lucky, the log messages will help us understand the problem.

4. Conclusion

In this article, we discussed all the steps involved in migrating a virtual machine to a physical one. Although conceptually it all boils down to cloning the contents of the virtual disk to a physical disk, some of the steps are far from trivial.

In our case, we looked at how to perform the migration to a USB flash drive. This is especially useful to verify the actual compatibility of the operating system with our hardware before overwriting an internal hard drive.