Skip to content

Instantly share code, notes, and snippets.

@drewdomi
Created October 3, 2025 05:00
Show Gist options
  • Select an option

  • Save drewdomi/36a419d5c3d2a21dc40cbea4bd9e31fe to your computer and use it in GitHub Desktop.

Select an option

Save drewdomi/36a419d5c3d2a21dc40cbea4bd9e31fe to your computer and use it in GitHub Desktop.

Fixing virt-manager VM Internet Connectivity Issues on Arch Linux

Problem Description

When using virt-manager on Arch Linux, virtual machines may successfully obtain IP addresses from the NAT network (typically 192.168.122.x) but fail to access the internet. This manifests as:

  • ✅ VM gets IP address from DHCP (e.g., 192.168.122.112/24)
  • ✅ VM can ping the gateway (192.168.122.1)
  • ✅ DNS resolution works inside the VM
  • ❌ VM cannot reach external IP addresses (e.g., 8.8.8.8)
  • ❌ Web browsing and external connectivity fails

Root Cause Analysis

The issue occurs because libvirt fails to automatically create the necessary iptables NAT rules for the virtual network. This can happen due to:

  1. Docker interference: Docker aggressively manages iptables rules and may conflict with libvirt's rule creation
  2. Service startup timing: libvirt network starting before proper iptables initialization
  3. Missing or incorrect FORWARD chain rules: Blocking rules processed before allowing rules
  4. NAT MASQUERADE rules not created: No network address translation for VM traffic

Environment Details

  • Host OS: Arch Linux
  • Virtualization: libvirt + virt-manager
  • Network Configuration: NAT network (virbr0 bridge)
  • VM Network: 192.168.122.0/24 subnet
  • Gateway: 192.168.122.1

Diagnostic Steps

1. Verify Network Configuration

# Check host network interfaces
ip -c addr show

# Check VM IP configuration (inside VM)
ip addr show
ip route show

Expected output shows:

  • Host has virbr0 bridge at 192.168.122.1/24
  • VM has IP in 192.168.122.x range
  • VM default route points to 192.168.122.1

2. Test Connectivity Levels

# From inside VM - test gateway connectivity
ping -c 4 192.168.122.1

# Test external IP (this will fail)
ping -c 4 8.8.8.8

# Test DNS resolution
nslookup google.com

3. Check Host Configuration

# Verify IP forwarding is enabled
cat /proc/sys/net/ipv4/ip_forward

# Check existing NAT rules
sudo iptables -t nat -L POSTROUTING -n -v

# Look for libvirt rules (likely missing)
sudo iptables -t nat -S | grep 192.168.122

4. Examine libvirt Network Status

# Check network status
sudo virsh net-list --all

# Verify network configuration
sudo virsh net-dumpxml default

Solution: Manual iptables Rule Configuration

Step 1: Enable IP Forwarding

# Enable IP forwarding (temporary)
sudo sysctl net.ipv4.ip_forward=1

# Make permanent
echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf

Step 2: Restart libvirt Network

# Restart the default network
sudo virsh net-destroy default
sudo virsh net-start default

# Verify it's running
sudo virsh net-list

Step 3: Add Required iptables Rules

# Clear any conflicting FORWARD rules
sudo iptables -F FORWARD

# Add NAT MASQUERADE rule for VM network
sudo iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE

# Add FORWARD rules (in correct order - ACCEPT before REJECT)
sudo iptables -I FORWARD 1 -d 192.168.122.0/24 -o virbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
sudo iptables -I FORWARD 2 -s 192.168.122.0/24 -i virbr0 -j ACCEPT
sudo iptables -I FORWARD 3 -i virbr0 -o virbr0 -j ACCEPT

# Restart Docker to restore its rules
sudo systemctl restart docker

Step 4: Verify Rules Are Applied

# Check NAT rules
sudo iptables -t nat -L POSTROUTING -n -v | grep 192.168.122

# Check FORWARD rules
sudo iptables -L FORWARD -n -v

# Ensure FORWARD policy allows traffic
sudo iptables -P FORWARD ACCEPT

Step 5: Test Connectivity

From inside the VM:

# Test external IP connectivity
ping -c 4 8.8.8.8

# Test web connectivity
curl -I google.com

# Should now work successfully

Making Changes Persistent

Save iptables Rules (Recommended)

The manual iptables rules will be lost after reboot. To make them persistent:

# Save current iptables rules
sudo iptables-save | sudo tee /etc/iptables/iptables.rules

# Enable iptables service to restore rules on boot
sudo systemctl enable iptables.service
sudo systemctl start iptables.service

# Ensure libvirt network autostarts
sudo virsh net-autostart default

Verify Persistence Setup

# Check that services are enabled
sudo systemctl is-enabled libvirtd
sudo systemctl is-enabled iptables

# Verify network autostart
sudo virsh net-list --autostart

Expected iptables Rules After Fix

NAT Table (POSTROUTING)

Chain POSTROUTING (policy ACCEPT)
MASQUERADE  all  --  *      *       192.168.122.0/24    !192.168.122.0/24
MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0  

Filter Table (FORWARD)

Chain FORWARD (policy ACCEPT)
ACCEPT     all  --  *      virbr0  0.0.0.0/0            192.168.122.0/24     ctstate RELATED,ESTABLISHED
ACCEPT     all  --  virbr0 *       192.168.122.0/24     0.0.0.0/0
ACCEPT     all  --  virbr0 virbr0  0.0.0.0/0            0.0.0.0/0

Troubleshooting Tips

If VM Still Can't Access Internet

  1. Check rule order: ACCEPT rules must come before REJECT rules in FORWARD chain
  2. Verify IP forwarding: cat /proc/sys/net/ipv4/ip_forward should return 1
  3. Check host connectivity: Ensure host can reach internet
  4. Review logs: sudo journalctl -u libvirtd -f

If Rules Don't Persist After Reboot

  1. Verify iptables service: sudo systemctl status iptables
  2. Check saved rules: cat /etc/iptables/iptables.rules
  3. Manual restore: sudo iptables-restore < /etc/iptables/iptables.rules

Alternative: Disable Docker Temporarily

If Docker is interfering:

# Stop Docker temporarily
sudo systemctl stop docker

# Restart libvirt
sudo systemctl restart libvirtd
sudo virsh net-start default

# Check if libvirt creates rules automatically
sudo iptables -t nat -L | grep 192.168.122

Prevention for Future Installations

To avoid this issue on new Arch Linux installations:

  1. Install in correct order:

    sudo pacman -S libvirt virt-manager
    sudo systemctl enable libvirtd
  2. Configure before starting VMs:

    echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
    sudo systemctl enable iptables
  3. Add user to libvirt group:

    sudo usermod -a -G libvirt $USER

Summary

This issue is a common problem on Arch Linux where libvirt's automatic iptables rule creation fails, often due to Docker interference or service timing issues. The solution involves manually creating the necessary NAT and FORWARD rules and making them persistent through the iptables service.

The key components of the fix are:

  • ✅ IP forwarding enabled on host
  • ✅ NAT MASQUERADE rule for VM subnet
  • ✅ FORWARD chain rules allowing VM traffic
  • ✅ Proper rule ordering (ACCEPT before REJECT)
  • ✅ Persistence through iptables service

After applying this fix, virtual machines will have full internet connectivity that persists across reboots.


Author: drewdomi
Date: 2025-10-03
Environment: Arch Linux with libvirt/virt-manager
Issue: VM NAT network internet connectivity
Status: ✅ Resolved

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment