libvirt gestiona las redes de las máquinas virtuales KVM con redes virtuales: cada una es un bridge de Linux en el anfitrión (virbr0, virbr1...) con, opcionalmente, DHCP y DNS servidos por dnsmasq y reglas de firewall para NAT o enrutamiento. Elegir el modo adecuado decide si tus VMs pueden salir a Internet, si son accesibles desde fuera y si pueden hablar entre ellas.

En este tutorial revisarás la red NAT por defecto, reservarás IPs fijas por DHCP, crearás una red aislada y una enrutada, y conectarás VMs directamente a la red física mediante un bridge configurado con netplan, todo en Ubuntu 24.04.

Requisitos previos

  • Un anfitrión con Ubuntu 24.04 LTS con KVM y libvirt instalados, y tu usuario en el grupo libvirt con LIBVIRT_DEFAULT_URI=qemu:///system. La guía de instalación de KVM/QEMU en Linux explica cómo prepararlo.
  • Al menos una VM. Los ejemplos usan vm1.
  • Para la red en puente: acceso a la consola del servidor (IPMI, KVM remoto o consola del proveedor), porque un error en netplan puede cortar la conexión SSH.

Modos de red de libvirt

ModoSalida a InternetAccesible desde fueraUso típico
NAT (por defecto)Sí, con la IP del anfitriónNoLaboratorios, VMs que solo necesitan salir
AisladaNoNoRedes internas entre VMs, bases de datos, pruebas
EnrutadaSí, sin NATSí, si la red externa tiene ruta hacia la subredSubredes propias enrutadas al anfitrión
Puente (bridge)Sí, como un equipo más de la LANSíVMs con IP de la red física

Paso 1: Revisar la red NAT por defecto

Al instalar libvirt se crea la red default: NAT sobre 192.168.122.0/24, con el anfitrión en 192.168.122.1 haciendo de puerta de enlace, DHCP y DNS. Lista las redes:

virsh net-list --all
 Name      State    Autostart   Persistent
--------------------------------------------
 default   active   yes         yes

Muestra su definición:

virsh net-dumpxml default
<network>
  <name>default</name>
  <uuid>2b9f4c1e-6d0a-4f7e-9c3b-8a1d5e2f7c60</uuid>
  <forward mode='nat'>
    <nat>
      <port start='1024' end='65535'/>
    </nat>
  </forward>
  <bridge name='virbr0' stp='on' delay='0'/>
  <mac address='52:54:00:6e:21:4a'/>
  <ip address='192.168.122.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.122.2' end='192.168.122.254'/>
    </dhcp>
  </ip>
</network>

En el anfitrión, la red es el bridge virbr0:

ip -brief addr show virbr0
virbr0           UP             192.168.122.1/24

Las concesiones DHCP muestran qué IP tiene cada VM:

virsh net-dhcp-leases default
 Expiry Time           MAC address         Protocol   IP address          Hostname   Client ID or DUID
-----------------------------------------------------------------------------------------------------------
 2026-09-25 12:41:05   52:54:00:3b:8a:12   ipv4       192.168.122.57/24   vm1        ff:56:50:4d:98:...

libvirt activa por sí mismo el reenvío IP (net.ipv4.ip_forward) y añade las reglas de NAT al arrancar la red; no hace falta configurarlas a mano.

Paso 2: Reservar una IP fija por DHCP

Para que una VM tenga siempre la misma IP sin configurarla dentro del invitado, crea una reserva DHCP ligada a su MAC. Obtén la MAC de la VM:

virsh domiflist vm1
 Interface   Type      Source    Model    MAC
-------------------------------------------------------------
 vnet0       network   default   virtio   52:54:00:3b:8a:12

Añade la reserva a la red default. --live la aplica a la red en marcha y --config la guarda de forma persistente:

virsh net-update default add ip-dhcp-host "<host mac='52:54:00:3b:8a:12' name='vm1' ip='192.168.122.10'/>" --live --config

Comprueba que aparece en la definición:

virsh net-dumpxml default | grep "host mac"
      <host mac='52:54:00:3b:8a:12' name='vm1' ip='192.168.122.10'/>

La VM recibirá la nueva IP cuando renueve la concesión. Reiníciala o renueva DHCP desde dentro, y verifica con virsh domifaddr vm1. Elige IPs fijas que no choquen con otras reservas; si quieres separarlas del rango dinámico, reduce el <range> de la red con virsh net-edit default.

Paso 3: Crear una red aislada

Una red aislada conecta VMs entre sí y con el anfitrión, sin salida a Internet. Es útil para el tráfico interno, por ejemplo entre una aplicación y su base de datos. Crea el archivo XML:

nano ~/red-interna.xml
<network>
  <name>interna</name>
  <bridge name='virbr10' stp='on' delay='0'/>
  <ip address='10.10.10.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='10.10.10.100' end='10.10.10.200'/>
    </dhcp>
  </ip>
</network>

Al no tener elemento <forward>, libvirt no crea reglas de NAT ni de reenvío. Define la red, iníciala y márcala para arrancar con el sistema:

virsh net-define ~/red-interna.xml
virsh net-start interna
virsh net-autostart interna

Verifica que está activa:

virsh net-list
 Name      State    Autostart   Persistent
--------------------------------------------
 default   active   yes         yes
 interna   active   yes         yes

Añade a vm1 una segunda interfaz conectada a esta red, en caliente y de forma persistente:

virsh attach-interface vm1 network interna --model virtio --live --config

Dentro de la VM aparecerá una nueva interfaz (por ejemplo enp7s0). En las imágenes cloud de Ubuntu, netplan solo configura por DHCP la interfaz inicial, así que añade la nueva en el invitado:

sudo nano /etc/netplan/60-interna.yaml
network:
  version: 2
  ethernets:
    enp7s0:
      dhcp4: true
      dhcp4-overrides:
        use-routes: false

use-routes: false evita que esta interfaz añada una ruta por defecto que compita con la de la red NAT. Aplica la configuración en la VM y comprueba la IP:

sudo chmod 600 /etc/netplan/60-interna.yaml
sudo netplan apply
ip -brief addr

Sustituye enp7s0 por el nombre real que muestre ip link en tu VM. Repite con otra VM y comprueba que se ven con ping entre sus IPs 10.10.10.x.

Paso 4: Crear una red enrutada

En modo enrutado, el anfitrión reenvía el tráfico de la subred de las VMs sin NAT: las VMs salen con sus propias IPs. Tiene sentido cuando dispones de una subred que tu router o proveedor enruta hacia la IP del anfitrión. Si nadie en la red externa sabe llegar a esa subred, el tráfico de vuelta se perderá.

Crea la definición, sustituyendo 10.20.0.0/24 por tu subred:

nano ~/red-enrutada.xml
<network>
  <name>enrutada</name>
  <forward mode='route'/>
  <bridge name='virbr20' stp='on' delay='0'/>
  <ip address='10.20.0.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='10.20.0.100' end='10.20.0.200'/>
    </dhcp>
  </ip>
</network>

Define e inicia la red:

virsh net-define ~/red-enrutada.xml
virsh net-start enrutada
virsh net-autostart enrutada

En el router de tu red, añade una ruta estática hacia 10.20.0.0/24 con la IP del anfitrión como siguiente salto. Si el router es otro servidor Linux, sería:

sudo ip route add 10.20.0.0/24 via your_server_ip

Para comprobarlo, conecta una VM a la red enrutada y haz ping a su IP 10.20.0.x desde un equipo de la red externa.

Paso 5: Conectar las VMs a la red física con un bridge

En modo puente, las VMs se conectan a la misma red que la interfaz física del anfitrión y obtienen IPs de esa red, como cualquier otro equipo. Hay que convertir la interfaz física en miembro de un bridge br0 que pasa a tener la IP del anfitrión.

Identifica la interfaz física y la configuración actual de netplan:

ip -brief link
ls /etc/netplan/

Haz una copia de seguridad del archivo actual (el nombre puede variar, por ejemplo 50-cloud-init.yaml):

sudo cp /etc/netplan/50-cloud-init.yaml ~/50-cloud-init.yaml.bak

Edita la configuración para que la IP pase de la interfaz física al bridge. Este ejemplo usa la interfaz eno1 y una IP estática; sustituye las direcciones por las tuyas:

sudo nano /etc/netplan/50-cloud-init.yaml
network:
  version: 2
  ethernets:
    eno1:
      dhcp4: false
  bridges:
    br0:
      interfaces:
        - eno1
      addresses:
        - 192.168.1.50/24
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses:
          - 1.1.1.1
          - 9.9.9.9
      parameters:
        stp: false
        forward-delay: 0

Si el archivo lo genera cloud-init, evita que lo sobrescriba en el siguiente arranque:

echo 'network: {config: disabled}' | sudo tee /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg

Aplica los cambios con netplan try, que revierte la configuración automáticamente si no confirmas con Enter en 120 segundos. Así no te quedas sin acceso si algo falla:

sudo netplan try

Verifica que la IP está ahora en br0:

ip -brief addr show br0
br0              UP             192.168.1.50/24

Crea una red de libvirt que use ese bridge, para poder conectar VMs por nombre de red:

nano ~/red-puente.xml
<network>
  <name>puente</name>
  <forward mode='bridge'/>
  <bridge name='br0'/>
</network>
virsh net-define ~/red-puente.xml
virsh net-start puente
virsh net-autostart puente

En este modo libvirt no ofrece DHCP: la VM obtendrá la IP del DHCP de tu red o tendrás que configurarla estática dentro del invitado. Conecta una VM:

virsh attach-interface vm1 network puente --model virtio --live --config

Comprueba desde otro equipo de la LAN que llegas a la IP que haya recibido la VM.

Paso 6: Gestionar y eliminar redes

Para editar una red persistente, usa net-edit. La mayoría de los cambios se aplican al reiniciar la red, lo que desconecta momentáneamente las VMs que la usan:

virsh net-edit interna
virsh net-destroy interna
virsh net-start interna

Tras reiniciar una red, las VMs en marcha pierden su conexión con el bridge. Reconéctalas apagándolas y arrancándolas con virsh shutdown y virsh start.

Para eliminar una red, detenla y borra su definición:

virsh net-destroy enrutada
virsh net-undefine enrutada

Antes, desconecta las interfaces de las VMs que la usen con virsh detach-interface vm1 network --mac 52:54:00:xx:xx:xx --live --config, usando la MAC que muestre virsh domiflist vm1.

Solución de problemas

  • error: Failed to start network ... Network is already in use by interface: la subred de la red choca con otra interfaz del anfitrión. Elige otra subred en el XML.
  • virsh net-start falla por el bridge br0 en modo puente: el bridge no existe en el sistema. Comprueba con ip link show br0 que netplan lo creó.
  • Las VMs no obtienen IP en una red NAT o aislada: comprueba que la red está active con virsh net-list y que dnsmasq está en marcha con ps aux | grep dnsmasq. Revisa si el invitado usa DHCP en esa interfaz.
  • Las VMs en NAT no salen a Internet tras instalar un firewall: libvirt añade sus reglas al arrancar la red; si recargaste el firewall después, reinicia libvirt con sudo systemctl restart libvirtd para que vuelva a crearlas.
  • Te quedas sin conexión al configurar el bridge: si no confirmaste netplan try, la configuración se revierte sola. Si usaste netplan apply, entra por la consola del servidor y restaura la copia de seguridad.

Conclusión

Has revisado la red NAT de libvirt, fijado IPs por DHCP, creado una red aislada para tráfico interno, una red enrutada sin NAT y un bridge para conectar VMs a la red física. Como siguientes pasos:

  • Publica servicios de VMs en red NAT con un proxy inverso (Nginx o HAProxy) en el anfitrión.
  • Combina una red aislada para el tráfico interno con NAT o puente solo en las VMs que lo necesiten.
  • Consulta la documentación de formato de redes de libvirt (https://libvirt.org/formatnetwork.html) para IPv6, DNS y otras opciones.