Migrating a Virtual Machine from GNOME Boxes to Virt-Manager: A Troubleshooting Journey Through Libvirt Networking and Virtualization

Migrating a Virtual Machine from GNOME Boxes to Virt-Manager: A Troubleshooting Journey Through Libvirt Networking and Virtualization

Published on 06/23/2026 · Cloud & Virtualization

Step-by-step guide to migrating a VM from GNOME Boxes to Virt-Manager on Fedora Server. Learn Libvirt networking, KVM troubleshooting, virtual bridges, NAT debugging, and Linux virtualization best practices.

Why My Virtual Machine Had No Internet: Migrating a Rocky Linux VM from GNOME Boxes to Virt-Manager on Fedora Server

Virtualization on Linux is usually boring—in the best possible way. Once configured correctly, KVM, QEMU, Libvirt, and Virt-Manager tend to run for months or years without requiring much attention.

That's why troubleshooting virtualization issues can be so deceptive.

When something breaks, it's natural to assume there's a single root cause behind all symptoms. During a recent migration of a Rocky Linux virtual machine from GNOME Boxes on my Fedora Workstation to a Fedora Server running Virt-Manager, I learned that reality is often more complicated.

What initially appeared to be a single networking problem turned out to be two entirely different issues producing nearly identical symptoms.

The result was an investigation that crossed multiple layers of the Linux virtualization stack, including virtual networking, bridges, NAT, Firewalld, Libvirt daemons, storage pools, and remote Virt-Manager management.

More importantly, it reinforced a lesson that applies to every Linux administrator:

The troubleshooting methodology is often more valuable than the final fix.

In this article, I'll walk through the complete migration process, explain the logic behind each troubleshooting step, and show how systematically eliminating possibilities led to the real answers.


The Goal

The objective was straightforward.

I already had a fully functional Rocky Linux 10 virtual machine running on my Fedora Workstation through GNOME Boxes.

Instead of reinstalling the operating system from scratch on my Fedora Server, I wanted to migrate the existing virtual machine and preserve everything exactly as it was.

The ideal scenario was simple:

  • Copy the virtual disk
  • Import it into Virt-Manager
  • Boot the VM
  • Continue working

As you'll see, things became more interesting.


Finding the Virtual Disk

The first step was identifying the actual disk image used by GNOME Boxes.

Many Linux users don't realize that GNOME Boxes itself is simply a graphical frontend built on top of Libvirt and QEMU.

Because of that, I could inspect the virtual machine directly using Libvirt tools.

After locating the VM definition, I identified the disk image being used by the Rocky Linux guest.

The file was stored as a QCOW2 image.

This was excellent news.

A QCOW2 file is essentially a complete computer stored inside a single file. It contains the operating system, installed software, configuration, user data, and everything else required to boot the machine.

Rather than migrating an operating system, I was effectively migrating an entire computer.


Transferring the VM to the Server

The transfer itself was straightforward.

I copied the QCOW2 image from the workstation to the Fedora Server using rsync.

After the transfer completed, I moved the image into the standard Libvirt storage location:

/var/lib/libvirt/images

At this point, the virtual disk physically existed on the server.

But a disk alone isn't enough.

The virtualization infrastructure still needed to recognize and use it correctly.


Preparing the Virtualization Environment

Before importing the VM, I verified several critical components:

  • SELinux contexts
  • File ownership
  • Storage pools
  • Virtual networks
  • Virt-Manager remote access

One interesting issue appeared almost immediately.

When attempting to connect remotely through Virt-Manager, authentication failed.

The error message pointed toward a missing Polkit authorization path.

After investigation, the cause turned out to be surprisingly simple:

The user account wasn't a member of the libvirt group.

Adding the user to the appropriate group and reconnecting solved the issue immediately.

This was the first reminder that virtualization problems are not always virtualization problems.

Sometimes they're simply Linux permissions problems.


Importing the Existing VM

With storage pools configured and remote management working, I imported the existing QCOW2 image into Virt-Manager.

The import process was uneventful.

The VM appeared correctly.

CPU allocation was correct.

Memory allocation was correct.

The network adapter was present.

Everything looked healthy.

Then I powered it on.

The operating system booted successfully.

The virtual NIC appeared.

DHCP assigned an IP address.

At first glance, the migration seemed complete.

Then I tried:

ping 8.8.8.8

And it failed.


The Most Common Troubleshooting Mistake

When a virtual machine can't reach the internet, most people immediately begin checking:

  • DNS
  • Firewalld
  • NAT
  • Docker
  • SELinux

While these are reasonable suspects, jumping directly to solutions often creates confusion.

Instead of asking:

"What should I fix?"

I asked:

"Where exactly does communication stop working?"

That single question shaped the entire investigation.


First Suspect: SELinux

Fedora uses SELinux in enforcing mode by default.

It's often blamed whenever something behaves unexpectedly.

To test the hypothesis, I temporarily disabled enforcement.

Nothing changed.

Connectivity remained broken.

SELinux was eliminated from the list.


Second Suspect: Firewalld

The next candidate was Firewalld.

Perhaps a forwarding rule was missing.

Perhaps a zone assignment was incorrect.

I temporarily stopped Firewalld.

Again, nothing changed.

The VM still couldn't reach the internet.

Firewalld was not the culprit.


Third Suspect: NAT

Since the virtual network was operating in NAT mode, this seemed like a logical area to investigate.

NAT problems can easily prevent guests from reaching external networks.

I started examining forwarding and translation behavior.

However, before diving too deeply into NAT, I decided to ask a more fundamental question.

Could the VM even reach its gateway?


The Discovery That Changed Everything

Inside the virtual machine, I tested connectivity to the gateway:

ping 192.168.122.1

It failed.

This completely changed the investigation.

If the VM cannot reach its gateway:

  • DNS is irrelevant.
  • Internet routing is irrelevant.
  • NAT is irrelevant.
  • External connectivity is irrelevant.

The failure exists at a much lower layer.

At that moment, the problem stopped being "internet access."

It became a local virtual networking problem.


Looking Below Layer 3

The investigation shifted toward the virtual networking infrastructure itself.

The next step was examining the Linux bridge configuration.

Using:

bridge link

I expected to see the VM's virtual interface attached to the Libvirt bridge.

Instead, it wasn't there.

The TAP interface existed.

The bridge existed.

But the TAP interface wasn't attached to the bridge.

That explained everything.

Without that attachment, the VM was effectively disconnected from its own virtual network.


The Decisive Test

Rather than theorizing endlessly, I performed a direct experiment.

I manually attached the interface to the bridge.

Immediately afterward:

ping 192.168.122.1

Started working.

The VM could reach its gateway.

Internal communication was restored.

The problem had been isolated.

I still didn't know why it was happening.

But I now knew exactly where it was happening.

That's a huge difference.


Following the Evidence Into Libvirt

Once the failure point was identified, the next step was examining Libvirt itself.

System logs revealed errors involving one of Libvirt's modular daemons.

Specifically:

virtnodedevd

was not running.

Additional verification showed that other Libvirt-related daemons also required attention.

The following services were enabled and started:

  • virtqemud
  • virtnetworkd
  • virtstoraged
  • virtnodedevd

At this point, the virtualization stack appeared complete.

Yet some symptoms still persisted.

That raised an important possibility.

Maybe there wasn't just one problem.


The Earlier Problem That Complicated Everything

Several days before migrating the VM, I had already encountered another networking issue involving Libvirt.

The symptoms looked almost identical.

The VM received an IP address.

The network appeared operational.

Yet internet access failed.

At first glance, it seemed to be the same problem.

It wasn't.


Similar Symptoms, Different Causes

In the earlier case:

VM → Gateway → No Internet

In the migration case:

VM → Cannot Reach Gateway

The final symptom looked identical:

No Internet Access

But the underlying causes existed in completely different layers of the network stack.

This distinction proved critical.


Using Tcpdump to Follow the Packets

For the earlier networking issue, packet captures became the breakthrough.

I captured traffic on the virtual bridge.

Then I captured traffic on the physical network interface.

The results were revealing.

Packets from the VM arrived at the virtual bridge.

But they never reached the physical NIC.

Somewhere inside the host, forwarding stopped.

Now I knew where to look.


Investigating NAT Rules

The next step involved examining nftables and iptables.

That's where the real anomaly appeared.

Docker's MASQUERADE rules were present.

Libvirt's MASQUERADE rules were missing.

The virtual network existed.

The bridge existed.

DHCP worked.

But the NAT rules required for internet access had not been installed correctly.

The VM could communicate internally.

It simply couldn't leave the host.


Implementing the Temporary Fix

To verify the theory, I manually created forwarding and MASQUERADE rules.

The result was immediate.

Internet connectivity returned.

DNS began functioning.

External traffic flowed normally.

The diagnosis was confirmed.

The missing NAT configuration was responsible for the earlier outage.


The Most Valuable Lesson

The most important takeaway from this experience wasn't about Libvirt.

It wasn't about Firewalld.

It wasn't even about virtualization.

It was about methodology.

Many administrators approach troubleshooting by applying random fixes until something works.

I prefer a different question:

"What is the last thing that still works?"

Every answer reduces the search space.

Every successful test narrows the possibilities.

Eventually, the problem has nowhere left to hide.


The Troubleshooting Sequence That Worked

The process followed a strict progression:

  1. Confirm the symptom.
  2. Verify gateway connectivity.
  3. Verify virtual network connectivity.
  4. Verify bridge attachment.
  5. Verify forwarding.
  6. Verify NAT.
  7. Verify DNS.
  8. Verify external services.

Each layer depended on the previous one.

Skipping layers would have made the investigation significantly longer.


Why This Approach Scales

This methodology works far beyond virtualization.

It applies equally to:

  • Linux servers
  • Containers
  • Kubernetes clusters
  • VPNs
  • Firewalls
  • Cloud infrastructure

The principle is always the same:

Identify the last known working point.

Move one layer forward.

Test again.

Repeat until the failure appears.


Conclusion

What started as a simple Rocky Linux VM migration became a deep exploration of the Linux virtualization stack.

Along the way, two entirely different networking failures were uncovered:

  • Missing Libvirt NAT rules preventing internet access.
  • A virtual interface that was not properly attached to its bridge.

Both produced the same visible symptom.

Neither shared the same root cause.

The experience reinforced one of the most important lessons in Linux administration:

Symptoms tell you where to start looking.

Methodical troubleshooting tells you where the problem actually is.

And when you're working with KVM, QEMU, Libvirt, and Virt-Manager, that distinction can save hours of frustration.