Problema

Los ingenieros que entrenan modelos de lenguaje, ejecutan inferencias de 70 B + parámetros o corren contenedores de inferencia suelen recurrir a servicios en la nube. El coste mensual y la dependencia de APIs externas son obstáculos cuando se busca experimentar sin compromisos financieros ni de privacidad. La necesidad típica es disponer de una GPU dedicada que pueda ser usada tanto por una máquina virtual (para aplicaciones Windows) como por contenedores Linux en el mismo host, todo dentro de la red doméstica.

En un entorno de homelab, el patrón que se repite es:

  1. Un servidor Linux con soporte de virtualización.
  2. Una o más GPU que deben estar disponibles para una VM (passthrough) y, al mismo tiempo, para workloads nativos en Linux.
  3. Acceso remoto seguro para la VM y los contenedores.
  4. Un proceso reproducible que permita volver a levantar el entorno después de una falla o una actualización de hardware.

El reto es alinear BIOS, kernel, libvirt y drivers de Windows sin crear cuellos de botella ni perder la capacidad de usar Docker para los servicios de IA.

Causa

Los fallos más habituales aparecen por:

  • IOMMU desactivado en la BIOS o en el kernel, impidiendo que el hipervisor reasigne dispositivos PCIe.
  • Grupos de IOMMU que mezclan la GPU con otros dispositivos (USB, audio) y provocan “device is busy” al intentar el passthrough.
  • Controladores de la GPU en el host que siguen cargados, bloqueando la exclusión de la tarjeta.
  • Configuración de libvirt incompleta: falta de vfio-pci en host-passthrough, o ausencia de machine con q35 y cpu con host-passthrough.
  • Windows 11 que requiere firmas de driver y, a veces, un reinicio de la GPU que el host no permite sin reset o ACS (Access Control Services) habilitado.
  • Redirección de pantalla: sin un monitor conectado o sin “dummy HDMI”, la GPU entra en modo de bajo consumo y la VM no recibe señal.
  • Problemas de red cuando se combina VNC con túnel SSH: puertos mal mapeados o firewall bloqueando el tráfico.

Solución

Una arquitectura reutilizable se basa en tres capas:

  1. Host Linux (Ubuntu 26.04 o similar) – habilita IOMMU, prepara vfio-pci, y asegura que la GPU no sea gestionada por el driver nativo.
  2. Máquina virtual Windows 11 – configurada con libvirt para usar host-passthrough y con una pantalla virtual (VNC) accesible vía SSH.
  3. Servicios Docker – corren en el host, consumen la segunda GPU (si la hay) o la CPU, y se exponen mediante docker compose.

Paso 1: Preparar el host

  1. Activar IOMMU
    En la BIOS, habilita VT‑d (Intel) o AMD‑IOMMU.
    Añade al kernel: intel_iommu=on o amd_iommu=on según corresponda.

  2. Configurar GRUB

    sudo nano /etc/default/grub
    

    Modifica la línea GRUB_CMDLINE_LINUX_DEFAULT para incluir:

    intel_iommu=on iommu=pt
    

    o la variante AMD. Luego:

    sudo update-grub && sudo reboot
    
  3. Verificar grupos IOMMU

    sudo dmesg | grep -e DMAR -e IOMMU
    

    Busca que cada dispositivo tenga su propio group.

  4. Aislar la GPU
    Identifica el PCI ID de la tarjeta que será pasada:

    lspci -nn | grep -i nvidia
    

    Supongamos 01:00.0. Añade al archivo de módulos:

    sudo nano /etc/modprobe.d/vfio.conf
    

    Contenido:

    options vfio-pci ids=10de:2488,10de:228b
    

    (reemplaza con los IDs de tu modelo).
    Desactiva los drivers nativos:

    echo "blacklist nouveau" | sudo tee -a /etc/modprobe.d/blacklist.conf
    sudo update-initramfs -u && sudo reboot
    
  5. Instalar KVM y libvirt

    sudo apt install -y qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager
    sudo systemctl enable --now libvirtd
    

Paso 2: Definir la VM

Crea una definición XML mínima y añádela a libvirt:

sudo virsh define /tmp/windows11.xml

Contenido esencial (/tmp/windows11.xml):

<domain type='kvm'>
  <name>win11-ai</name>
  <memory unit='MiB'>16384</memory>
  <vcpu placement='static'>8</vcpu>
  <os>
    <type arch='x86_64' machine='q35'>hvm</type>
    <loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
    <nvram>/var/lib/libvirt/qemu/nvram/win11-ai_VARS.fd</nvram>
  </os>
  <features>
    <acpi/>
    <apic/>
    <hyperv>
      <relaxed state='on'/>
      <vapic state='on'/>
      <spinlocks state='on' retries='8191'/>
    </hyperv>
  </features>
  <cpu mode='host-passthrough' check='none'/>
  <devices>
    <hostdev mode='subsystem' type='pci' managed='yes'>
      <source address='0000:01:00.0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0'/>
    </hostdev>
    <hostdev mode='subsystem' type='pci' managed='yes'>
      <source address='0000:01:00.1'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x09' function='0x0'/>
    </hostdev>
    <graphics type='vnc' port='-1' listen='127.0.0.1' keymap='es'/>
    <sound model='ich9'/>
    <input type='tablet' bus='usb'/>
  </devices>
</domain>
  • La sección <hostdev> incluye la GPU y su controlador de audio.
  • graphics abre VNC en un puerto aleatorio; el acceso se hará mediante túnel SSH.

Arranca la VM y procede con la instalación de Windows 11 (ISO en /var/lib/libvirt/images). Instala los drivers de NVIDIA desde la web oficial; el instalador detectará la GPU como “passthrough”.

Paso 3: Acceso remoto seguro

En el cliente, abre un túnel SSH:

ssh -L 5901:127.0.0.1:5901 usuario@host-ip

Luego, con un visor VNC (virt-viewer o remmina) conecta a localhost:5901. La VM queda accesible desde cualquier dispositivo dentro de la red, siempre que el túnel esté activo.

Paso 4: Contenedores Docker para servicios de IA

En el host, crea un docker-compose.yml que lance, por ejemplo, Ollama, vLLM o Text Generation Inference:

version: '3.8'
services:
  ollama:
    image: ollama/ollama:latest
    restart: unless-stopped
    ports:
      - "11434:11434"
    volumes:
      - ollama-data:/root/.ollama
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
volumes:
  ollama-data:

Asegúrate de que nvidia-container-toolkit esté instalado y que el daemon de Docker reconozca la segunda GPU (si la hay) o la CPU.

sudo apt install -y nvidia-docker2
sudo systemctl restart docker
docker compose up -d

Con esto, los modelos se sirven en la red local sin tocar la VM.

Cuándo aplicar esta solución

  • Necesitas GPU dedicada para Windows (por ejemplo, para software que solo corre en ese SO) y al mismo tiempo contenedores Linux que consuman otra GPU o la CPU.
  • Presupuesto limitado: prefieres hardware propio a servicios en la nube.
  • Privacidad: los datos nunca abandonan tu red local.
  • Escalabilidad moderada: el host tiene al menos 16 GB RAM y una GPU con 12 GB VRAM o más.

No es apropiado cuando:

  • Solo se requiere inferencia en Linux; en ese caso, el passthrough a Windows es sobre‑ingeniería.
  • El hardware no soporta IOMMU o la GPU no permite reset de forma fiable (modelos antiguos).
  • Se necesita alta disponibilidad multi‑nodo; KVM en un solo host no brinda redundancia.

Código

# 1. Habilitar IOMMU