Nada humilla más a un administrador confiado que una VM Windows que “está bien” en la teoría y es un desastre en la práctica. Haces clic en Iniciar y tarda en reaccionar. Abres una app y parece negociar. La longitud de la cola de disco parece la fila de la mañana para el café.
Proxmox puede ejecutar Windows rápido—aburridamente rápido. Pero tienes que girar los pocos mandos correctos. No todos los mandos. Ocho de ellos. Y debes validar con medidas en lugar de sensaciones, porque Windows puede ocultar el problema de almacenamiento detrás de “System” en CPU y Proxmox puede ocultarlo tras “io=…”.
Guía de diagnóstico rápido (encuentra el cuello de botella en 10 minutos)
Cuando una VM Windows está lenta, tu instinto es culpar a Windows. Tu trabajo es identificar qué capa está realmente esperando: planificación de CPU del invitado, encolamiento de almacenamiento en el invitado, emulación de dispositivo de QEMU, IO en el host o el backend de almacenamiento. La ruta más rápida es triangular con la latencia, no con el rendimiento bruto.
Primero: decide si es planificación de CPU o latencia de almacenamiento
- Revisa el comportamiento de steal/ready de la CPU en el host: si las vCPU no se planifican, todo se siente como melaza.
- Revisa la latencia de IO del host: si el IO es lento, Windows “se congela” de formas que parecen problemas de CPU.
Segundo: confirma que la VM usa los dispositivos y controladores correctos
- Windows en SATA/IDE o usando el controlador genérico de Microsoft es básicamente un impuesto que pagas para siempre.
- Controlador incorrecto + sin VirtIO = alta CPU por IO y colas poco profundas.
Tercero: aísla dónde se está construyendo la cola
- Cola en el invitado (alta longitud de cola de disco) pero disco del host inactivo: a menudo configuración de controlador/driver o desajuste de modo de caché.
- Cola en el host (alto await en iostat) y el invitado también lento: latencia del backend de almacenamiento.
- Cola en el hilo de QEMU (alto uso de un solo núcleo en el host): falta iothread o controlador incorrecto.
Orden mínimo de operaciones (haz esto en orden)
- En el host: revisa latencia de IO y contención de CPU (
iostat,pvesh/qmstatus,top). - En la configuración de la VM: verifica controlador (VirtIO SCSI), modo de caché, iothread, discard y tipo de CPU.
- Dentro de Windows: verifica que los controladores VirtIO estén instalados y en uso; revisa longitud de cola de disco y uso de storport.
- En el backend de almacenamiento: ZFS recordsize/volblocksize alineación, comportamiento sync, ajustes de Ceph RBD, o LVM cache y scheduler.
Si haces esas cuatro cosas, normalmente encontrarás al culpable antes de que alguien sugiera “dale más vCPUs”, que es como los problemas de rendimiento se convierten en incidentes de producción.
Hechos y contexto que explican por qué ocurre esto
- VirtIO existe porque emular hardware “real” es lento. KVM es rápido en virtualización de CPU; la emulación de dispositivos es donde pagas el precio.
- IDE es históricamente simple, no históricamente rápido. La emulación IDE es una muleta de compatibilidad; consume CPU por IO y tiene colas poco profundas.
- La pila de almacenamiento de Windows cambió entre versiones. Los controladores basados en Storport y las mejoras de encolamiento hacen que Windows moderno se beneficie más de controladores virtuales adecuados.
- Los modos de caché de escritura son un contrato de fiabilidad. “Writeback” puede ser un cohete o una trampa dependiendo de si el host puede garantizar semánticas de flush.
- ZFS es transaccional y honesto sobre escrituras sync. Si tu carga emite escrituras sync (muchos patrones de Windows lo hacen), ZFS te hará pagar a menos que diseñes para ello.
- Ceph intercambia latencia por resiliencia distribuida. Puede ser rápido, pero las escrituras aleatorias pequeñas son brutalmente sensibles al jitter de red y al tuning de backend.
- NVMe no fue diseñado para un solo hilo de IO. Los SSD modernos quieren muchas colas y un alto paralelismo; un camino de IO single-threaded se convierte en el nuevo cuello de botella.
- Ballooning se diseñó para densidad, no para la felicidad del sistema. Puede hacer que una VM “entre” en el host, pero Windows reacciona mal a presión de memoria repentina.
- Los planificadores de IO y capas de caché pueden pelear entre sí. Dos capas “optimizando” el mismo flujo de IO a menudo significan que ninguna lo hace bien.
Una idea parafraseada atribuida a Werner Vogels (CTO de Amazon): Todo falla, así que diseña sistemas para seguir funcionando cuando partes inevitablemente se rompan.
Tuning de rendimiento es similar: supone que algo se va a atascar y reduce el radio de impacto.
Los 8 ajustes que realmente hacen la diferencia
1) Bus y controlador de disco: VirtIO SCSI (y cuándo no)
Si el disco de tu VM Windows está en IDE o SATA en Proxmox, ya encontraste un problema. Esas son elecciones de compatibilidad, no de rendimiento. El “SCSI” por defecto puede ser bueno, pero el tipo de controlador importa.
Mejor valor por defecto para Windows en Proxmox: VirtIO SCSI (usualmente virtio-scsi-single por simplicidad) más el controlador de almacenamiento VirtIO en Windows.
Por qué importa: VirtIO es un dispositivo paravirtualizado. Reduce las salidas de VM, reduce la CPU por IO y soporta colas más profundas. Las cargas Windows—especialmente “muchas lecturas pequeñas” durante el arranque y actualizaciones—se benefician enormemente.
Cuándo podrías elegir otra cosa:
- Disco virtual NVMe puede ser excelente para cargas con alta IOPS porque encaja bien con el encolamiento moderno. También es más simple que SCSI en algunos casos.
- SATA podría ser apropiado si estás atrapado con un instalador antiguo de Windows y no quieres inyectar controladores—solo como puente temporal.
Toma de opinión: no lances un “puente SATA temporal” a producción. Así es como lo temporal se vuelve permanente, como el cartón en una puerta rota que sigue ahí dos años después.
2) Modo de caché: elige uno y entiende el radio de impacto
El modo de caché decide quién puede mentir sobre durabilidad. A Windows le importa. A las bases de datos les importa más. Y a tu futuro yo le importa aún más.
Modos de caché comunes que verás en Proxmox para discos:
- cache=none: Generalmente el valor por defecto más seguro. Usa IO directo, evita doble caché y respeta correctamente las semánticas de flush. A menudo lo mejor para ZFS y Ceph.
- cache=writeback: Escrituras percibidas como más rápidas porque la caché de páginas del host las absorbe. Mayor riesgo si hay pérdida de energía o crash del host y no tienes protección de energía y manejo de flush correctos.
- cache=writethrough: Más seguro que writeback, a menudo más lento.
Cómo se percibe la lentitud cuando la caché está mal: pausas aleatorias, gran varianza en la latencia de IO, o benchmarks excelentes que se colapsan con carga real porque los flushes forzan la verdad en la línea temporal.
Recomendación: empieza con cache=none, mide, y luego considera writeback solo cuando puedas defender la historia de durabilidad (UPS, SSD empresarial con PLP, y comprensión del stack de almacenamiento).
3) Hilo IO: separa el camino lento de todo lo demás
QEMU tiene hilos. La emulación de dispositivo de almacenamiento y el procesamiento de virtqueue pueden convertirse en un solo carril caliente si no les das su propio carril. Eso es lo que hacen los iothread: aislar el procesamiento de IO de disco del hilo principal de vCPU.
Síntomas cuando necesitas iothread:
- La VM se siente “lenta” bajo carga de disco aunque el uso de CPU parezca moderado.
- Un núcleo de CPU del host se consume al máximo durante IO pesado, mientras otros están tranquilos.
- Picos de latencia que se correlacionan con tareas de fondo (Windows Defender, actualizaciones, indexado).
Habilita un iothread para el disco (o usa un controlador que lo soporte limpiamente). Este es uno de esos cambios poco glamorosos pero que frecuentemente muestran un antes/después obvio.
4) Discard/TRIM y emulación SSD: deja de mentirle al invitado
Windows asume que puede decirle al almacenamiento qué bloques están libres (TRIM). Si el invitado no puede hacer discard, el backend puede degradarse lentamente, especialmente en volúmenes thin-provisioned o pools SSD que dependen de saber qué está libre.
Habilita discard en el disco virtual si tu backend lo soporta (ZFS zvols pueden, Ceph RBD puede, LVM-thin puede). Luego habilita la optimización en Windows (normalmente lo hace automáticamente en “SSD”).
También considera la emulación SSD: hacer saber al invitado que está sobre un SSD puede cambiar el comportamiento de Windows (desfragmentación vs optimizar/trim programado). No es magia, pero evita trabajo innecesario.
Ten cuidado: discard no es gratis. En algunos backends puede generar carga. El punto es la corrección primero, luego medir.
5) Controladores VirtIO y herramientas guest: el sistema nervioso de la VM
Los problemas de rendimiento adoran los controladores faltantes. Windows funcionará con controladores genéricos, pero funcionará como un coche de alquiler con el freno de mano puesto.
Dentro de Windows quieres:
- Controlador de almacenamiento VirtIO (viostor o vioscsi dependiendo del controlador)
- Controlador de red VirtIO (NetKVM)
- Driver de balloon solo si usas ballooning intencionalmente (muchos entornos no deberían)
- Agente QEMU guest para mejores operaciones de ciclo de vida y, a veces, mejor comportamiento de temporización
No “montes la ISO de drivers y la olvides”. Confirma que el controlador está activo en el Administrador de dispositivos y que el disco usa el controlador esperado.
Broma #1: Windows sin controladores VirtIO es como un deportivo entregado con ruedas cuadradas—técnicamente se mueve, pero nadie está contento.
6) Tipo de CPU y NUMA: deja de dejar a Windows sin recursos
Si tu VM Windows es lenta bajo carga, podrías estar pagando por flags de CPU conservadores o una topología pobre. Proxmox ofrece múltiples tipos de CPU. La trampa segura-pero-lenta es usar un modelo genérico cuando “host” estaría bien.
Recomendación: usa CPU type: host para VMs sensibles al rendimiento en un clúster estable (o donde las restricciones de migración en caliente sean aceptables). Esto expone más características de CPU al invitado, mejorando rendimiento en crypto, compresión y algunos comportamientos del scheduler.
NUMA: Si das a una VM muchas vCPUs y RAM, pero abarca nodos NUMA sin conciencia, la latencia sube. Windows programará hilos entre nodos; si la topología de la VM no coincide, obtienes paradas aleatorias que parecen almacenamiento pero no lo son.
Regla práctica: no asignes una VM más grande que un nodo NUMA salvo que tengas una razón y entiendas la disposición del host.
7) Ballooning y swap de memoria: muerte por “utilidad”
Ballooning es seductor: “Podemos overcommit y estará bien.” Windows no está de acuerdo. Cuando se reclama memoria, Windows usa su propio paging, las cachés colapsan y la VM empieza a hacer IO de disco solo para compensar la falta de RAM. Así un problema de memoria se vuelve un problema de disco.
Recomendación: para VMs Windows sensibles al rendimiento, establece un tamaño de memoria fijo y desactiva ballooning a menos que tengas un proceso de gestión de capacidad probado.
En el host, asegúrate de que no estés intercambiando bajo carga. La actividad de swap en el host vuelve la latencia de los invitados caótica, y la latencia caótica es lo que los usuarios perciben como “lento”.
8) Afinado del backend de almacenamiento (ZFS/Ceph/LVM): alinea tamaños de bloque y colas
Aquí es donde la mayoría de los posts “mi VM está lenta” mueren: el backend hace exactamente lo que le pediste, no lo que querías. Proxmox abstrae almacenamiento, pero tus discos aún obedecen la física, la amplificación de escritura y las semánticas de flush.
ZFS (zvols y datasets)
- volblocksize importa para zvols. Si está mal ajustado a la carga, pagas en amplificación de escritura y latencia.
- escrituras sync son caras sin un SLOG apropiado. Windows puede generar patrones tipo sync (metadata, journaling, flushes de aplicaciones).
- compresión puede ayudar o perjudicar dependiendo de CPU y carga; CPUs modernas suelen hacerla rentable.
Ceph RBD
- Ceph puede entregar excelente throughput pero puede tener latencia base más alta que NVMe local.
- Las escrituras aleatorias pequeñas son sensibles al rendimiento de la red y de los OSD; “solo son 4k” no consuela.
- El cache de RBD y los ajustes de cliente pueden cambiar el comportamiento, pero empieza midiendo la distribución de latencia.
LVM-thin / SSD local
- La provisión thin necesita discard para evitar presión silenciosa de espacio.
- Revisa el scheduler del dispositivo subyacente y la profundidad de cola; los valores por defecto no siempre son óptimos.
- Monitorea el comportamiento de “write cliff” en SSD de consumo (agotamiento del SLC). Lo verás como picos repentinos de latencia.
Broma #2: Si tu backend de almacenamiento es un SSD de consumo sin protección contra pérdida de energía, “writeback cache” es solo acelerar la actualización de tu currículum.
Tareas prácticas: comandos, salidas y la decisión que tomas
Estas son las tareas que realmente ejecuto cuando una VM Windows se siente lenta en Proxmox. Cada una incluye un comando, una salida de ejemplo y la decisión que impulsa. Ejecútalas en el host Proxmox salvo que se indique lo contrario.
Task 1: Identify VM configuration (disk bus, cache, iothread, discard)
cr0x@server:~$ qm config 104
boot: order=scsi0;ide2;net0
cores: 8
cpu: x86-64-v2-AES
memory: 16384
name: win-app-01
net0: virtio=DE:AD:BE:EF:10:04,bridge=vmbr0,firewall=1
ostype: win11
scsi0: local-zfs:vm-104-disk-0,cache=none,discard=on,iothread=1,ssd=1,size=200G
scsihw: virtio-scsi-single
Qué significa: El disco está en VirtIO SCSI single, cache=none, discard=on, iothread habilitado, emulación SSD activada. Esta es la forma de “buenos valores por defecto”.
Decisión: Si ves ide0, sata0, cache=writeback sin una historia de durabilidad, o falta iothread=1, corrige la configuración antes de perseguir fantasmas.
Task 2: Confirm the VM is actually running and not memory balloon-thrashing
cr0x@server:~$ qm status 104 --verbose
status: running
cpus: 8
memory: 16384
balloon: 0
uptime: 91234
Qué significa: balloon: 0 implica que el ballooning está deshabilitado (memoria fija). Bueno para rendimiento predecible.
Decisión: Si ballooning está habilitado y el host está justo de RAM, apágalo para Windows sensibles a latencia o arregla la capacidad del host.
Task 3: Find which process/thread is burning CPU on the host during “disk slowness”
cr0x@server:~$ top -H -p $(pgrep -f "kvm.*104" | head -n1)
top - 10:03:11 up 12 days, 5:44, 2 users, load average: 6.10, 5.92, 5.80
Threads: 52 total, 1 running, 51 sleeping
%Cpu(s): 22.0 us, 3.0 sy, 0.0 ni, 75.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
31241 root 20 0 6143200 902144 21760 S 98.0 1.4 10:22.11 iothread
31200 root 20 0 6143200 902144 21760 S 12.0 1.4 2:13.45 CPU 0/KVM
31201 root 20 0 6143200 902144 21760 S 11.5 1.4 2:11.88 CPU 1/KVM
Qué significa: Un iothread está presente y está ocupado. Eso está bien si corresponde a carga real de disco. Si el hilo principal KVM está pegado en IO, probablemente necesites un hilo IO o un mejor controlador.
Decisión: Si un solo hilo de QEMU alcanza 100% durante IO, habilita iothread y asegura VirtIO SCSI/NVMe.
Task 4: Measure host disk latency and queueing
cr0x@server:~$ iostat -x 1 5
Linux 6.8.12 (pve01) 12/26/2025 _x86_64_ (32 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
10.12 0.00 2.31 8.44 0.00 79.13
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %util await r_await w_await
nvme0n1 220.0 180.0 8800.0 7200.0 0.0 5.0 92.0 18.2 9.1 29.0
Qué significa: await en dobles dígitos bajo carga sugiere latencia real. Si este es el dispositivo de respaldo de tu VM y Windows está “lento”, el backend probablemente es el límite.
Decisión: Si %util es alto y await es alto, arregla el almacenamiento (media más rápida, mejor diseño de pool, SLOG para sync, reducir amplificación de escritura). Si los discos del host están bien pero la VM es lenta, revisa config/drivers de la VM.
Task 5: Identify which storage device backs the VM disk
cr0x@server:~$ pvesm path local-zfs:vm-104-disk-0
/dev/zvol/rpool/data/vm-104-disk-0
Qué significa: El disco de la VM es un zvol de ZFS. Aplican reglas de tuning de ZFS.
Decisión: Si está en ZFS, revisa volblocksize, comportamiento sync y si el pool tiene un dispositivo de log rápido para cargas con muchas escrituras sync.
Task 6: Check ZFS pool health and latency signals
cr0x@server:~$ zpool status -v rpool
pool: rpool
state: ONLINE
scan: scrub repaired 0B in 00:18:44 with 0 errors on Sun Dec 22 03:12:18 2025
config:
NAME STATE READ WRITE CKSUM
rpool ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
errors: No known data errors
Qué significa: Pool sano. Salud no significa rápido, pero quita “resilvering degradado” como causa de rendimiento.
Decisión: Si el pool está degradado o en resilver, espera latencia. Arregla hardware/estado del pool primero; el tuning no ganará a una reconstrucción.
Task 7: Inspect ZFS dataset properties that affect VM disks
cr0x@server:~$ zfs get -o name,property,value -s local recordsize,compression,atime,sync rpool/data
NAME PROPERTY VALUE
rpool/data recordsize 128K
rpool/data compression lz4
rpool/data atime off
rpool/data sync standard
Qué significa: Recordsize aplica a datasets, no a zvols, pero esto indica intención general. Compresión activada y atime off suelen ser buenos por defecto.
Decisión: No “optimices” poniendo sync=disabled en almacenamiento de VM a menos que aceptes pérdida de datos en crash. Usa hardware adecuado en su lugar.
Task 8: Check zvol volblocksize (critical for Windows VM IO patterns)
cr0x@server:~$ zfs get -o name,property,value volblocksize rpool/data/vm-104-disk-0
NAME PROPERTY VALUE
rpool/data/vm-104-disk-0 volblocksize 8K
Qué significa: volblocksize de 8K suele ser un compromiso razonable para cargas VM generales. Muy grande puede amplificar escrituras pequeñas; muy pequeño puede aumentar overhead de metadata.
Decisión: Si ves volblocksize de 128K en un disco sistema Windows con muchas IO aleatorias pequeñas, considera recrear el disco con un volblocksize más pequeño y migrar.
Task 9: Observe ZFS IO and latency pressure in real time
cr0x@server:~$ zpool iostat -v rpool 1 5
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
rpool 1.20T 600G 800 650 60.0M 48.0M
mirror-0 1.20T 600G 800 650 60.0M 48.0M
nvme0n1 - - 420 330 31.0M 24.0M
nvme1n1 - - 380 320 29.0M 24.0M
Qué significa: Ves tasas reales de lectura/escritura y operaciones. Si Windows “se congela” mientras esos números suben, estás correlacionado con actividad de almacenamiento.
Decisión: Si el pool está saturado durante los stalls visibles al usuario, mueve vecinos ruidosos, añade vdevs más rápidos o separa cargas de trabajo.
Task 10: Check host memory pressure and swap (latency’s silent killer)
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 256Gi 210Gi 12Gi 2.0Gi 34Gi 38Gi
Swap: 16Gi 3.2Gi 13Gi
Qué significa: Swap está en uso. Puede ser histórico o un dolor activo.
Decisión: Si el swap crece durante la ventana de lentitud, deja de sobreaprovisionar memoria o mueve VMs. Swap en el host hace que la latencia del invitado parezca sabotaje aleatorio.
Task 11: See if the kernel is actively swapping under load
cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 3355443 1200000 8000 32000000 0 0 120 220 800 1200 12 3 77 8 0
3 1 3355443 1150000 8000 31500000 0 256 1024 4096 1200 1800 10 4 65 21 0
2 0 3355700 1100000 8000 31000000 128 512 900 3800 1100 1600 11 4 62 23 0
Qué significa: Si si/so no son cero indica swapping activo. Eso es un incidente de rendimiento en cámara lenta.
Decisión: Arregla la presión de memoria. No ajustes modos de caché para compensar swapping; es como cambiar la presión de neumáticos para arreglar un ruido en el motor.
Task 12: Check block device scheduler and queue settings (local disks)
cr0x@server:~$ cat /sys/block/nvme0n1/queue/scheduler
[mq-deadline] none
Qué significa: El scheduler es mq-deadline. Para NVMe esto suele estar bien. Para algunos arrays, “none” puede ser mejor; para cargas mixtas, un comportamiento tipo deadline ayuda la latencia cola.
Decisión: Si investigas latencia tail, prueba cambios de scheduler con cuidado y mide. No copies “none everywhere” sin pensar.
Task 13: Check VM disk options at the QEMU commandline level (sanity)
cr0x@server:~$ ps -ef | grep -E "kvm.*104" | head -n1
root 31200 1 18 09:40 ? 00:42:10 /usr/bin/kvm -id 104 -name win-app-01 -m 16384 -smp 8 -cpu x86-64-v2-AES ... -device virtio-scsi-pci,id=scsihw0 ... -drive file=/dev/zvol/rpool/data/vm-104-disk-0,if=none,id=drive-scsi0,cache=none,aio=io_uring,discard=unmap ...
Qué significa: Confirma los ajustes efectivos (cache, discard, aio). Esto atrapa desajustes entre lo que crees que Proxmox configuró y lo que QEMU realmente ejecuta.
Decisión: Si esperas discard=unmap y falta, revisa la configuración de hardware de la VM y el soporte del almacenamiento.
Task 14: Verify Ceph latency quickly (if using RBD)
cr0x@server:~$ ceph -s
cluster:
id: 9c1f3a1a-aaaa-bbbb-cccc-1234567890ab
health: HEALTH_OK
services:
mon: 3 daemons, quorum mon1,mon2,mon3
mgr: mgr1(active), standbys: mgr2
osd: 12 osds: 12 up, 12 in
data:
pools: 4 pools, 256 pgs
objects: 1.20M objects, 4.6 TiB
usage: 13 TiB used, 22 TiB / 35 TiB avail
pgs: 256 active+clean
Qué significa: HEALTH_OK no garantiza baja latencia, pero quita “cluster en llamas” de la lista.
Decisión: Si la salud no es OK (backfill, degradado, ops lentas), detén el tuning de la VM y arregla Ceph primero. Windows no vencerá operaciones lentas.
Task 15: Confirm Windows is using VirtIO storage driver (host-side check via agent)
cr0x@server:~$ qm agent 104 get-osinfo
{
"id": "mswindows",
"kernel-release": "10.0.22631",
"name": "Microsoft Windows 11 Pro",
"pretty-name": "Microsoft Windows 11 Pro",
"version": "11"
}
Qué significa: El agente guest funciona. Eso no es por sí solo una ganancia de rendimiento, pero habilita operaciones más fiables y visibilidad.
Decisión: Si el agente no está instalado, instálalo. Al diagnosticar rendimiento, tener menos puntos ciegos importa.
Errores comunes: síntoma → causa raíz → solución
1) “Windows está lento solo durante actualizaciones y reinicios”
Síntoma: Reinicios eternos, actualizaciones que tardan, “Trabajando en actualizaciones” parece una decisión de vida.
Causa raíz: Windows Update es intensivo en metadata y flush; controlador de disco equivocado, VirtIO faltante, o latencia sync de ZFS se expone.
Solución: Mueve el disco de sistema a VirtIO SCSI (o NVMe virtual), instala el controlador VirtIO de almacenamiento, usa cache=none y asegúrate de que el backend soporte escrituras sync (SLOG o medios adecuados).
2) “Los benchmarks son geniales, las apps reales son horribles”
Síntoma: Pruebas secuenciales muestran buen rendimiento; interacción de usuario y lanzamientos de apps se traban.
Causa raíz: Optimizaste para throughput, pero la carga es sensible a latencia tail. A menudo: cache writeback ocultando flushes, o camino IO single-threaded sin iothread.
Solución: Habilita iothread, prefiere cache=none y mide la distribución de latencia (await, no solo MB/s).
3) “Congelamientos aleatorios de 2–10 segundos, luego se recupera”
Síntoma: La VM “se cuelga” brevemente, especialmente bajo carga; retraso en mouse/teclado dentro de la consola.
Causa raíz: Swapping en el host, contención de grupos de transacciones ZFS, o picos de latencia del backend (cliff de escritura en SSD de consumo, Ceph ops lentas).
Solución: Evita swap en el host (más RAM, menos overcommit), asegura que el almacenamiento no esté saturado, revisa ops lentas/rebuild y evita ballooning.
4) “Uso alto de CPU pero se siente IO-bound”
Síntoma: Windows muestra alta CPU; el host muestra un núcleo ocupado; métricas de disco parecen moderadas.
Causa raíz: Overhead por emulación de dispositivos (IDE/SATA) o falta de controladores paravirtuales causando alta CPU por IO.
Solución: Cambia a VirtIO SCSI/NVMe e instala controladores correctos; habilita iothread.
5) “La red es lenta, pero solo en esta VM Windows”
Síntoma: Copias SMB lentas; otras VMs están bien.
Causa raíz: Uso de emulación e1000 o driver VirtIO net faltante; ajustes de offload desajustados.
Solución: Usa VirtIO net; instala NetKVM; confirma MTU y configuración del bridge; mide con iperf u herramientas similares (y no confundas disco con red).
6) “Todo iba bien hasta que habilitamos discard”
Síntoma: Picos periódicos de IO tras activar discard/trim.
Causa raíz: El backend trata discard como trabajo real (actualizaciones de metadata en thin provisioning, discards en Ceph o free space maps en ZFS) y lo has expuesto.
Solución: Mantén discard (la corrección importa), pero programa las tareas de optimización de Windows, considera limitar la tasa y asegúrate de que el backend esté dimensionado. Si los discards son patológicos, revisa la configuración del backend en lugar de desactivar discard a ciegas.
Tres mini-historias corporativas desde la trinchera de rendimiento
Mini-historia 1: El incidente provocado por una suposición equivocada
El ticket empezó como “Citrix está lento.” Siempre pasa. Los usuarios informaron que abrir Outlook dentro de una VM Windows tomaba minutos. Algunos admins insistieron que debía ser la nueva política antivirus, porque “el disco es SSD y la red está bien.” Esa suposición—“SSD implica baja latencia”—fue la semilla del incidente.
En el host Proxmox, iostat mostró throughput moderado pero feos picos de await. Las VMs Windows estaban en ZFS y el pool estaba sano. El detalle crítico: la carga era intensiva en sync debido al comportamiento de flush de la aplicación y la forma en que Windows actualiza metadata. No había dispositivo de log dedicado, y el pool se construyó sobre mirrors de NVMe de consumo sin protección contra pérdida de energía.
El equipo además había habilitado cache=writeback “por velocidad”, creyendo que era inofensivo porque “es SSD”. Bajo presión, el sistema osciló entre ráfagas rápidas y stalls cuando los flushes golpeaban. Los usuarios lo vivieron como congelamientos aleatorios. El host lo vivió como un dispositivo de almacenamiento que a veces tenía que decir la verdad.
La solución fue dolorosamente simple y políticamente molesta: revertir a cache=none, desplegar NVMe empresariales con PLP, y añadir un dispositivo de log rápido para el pool ZFS donde vivían las VMs sync-heavy. De repente las mismas VMs se comportaron como si pertenecieran a un centro de datos en lugar de una feria de ciencias.
La lección del postmortem no fue “ZFS es lento” ni “Windows es raro”. Fue que la pila tiene contratos. Si asumes que SSD equivale a durabilidad y baja latencia, terminas depurando expectativas humanas, no máquinas.
Mini-historia 2: La optimización que salió mal
Otra organización tenía un clúster Proxmox mixto: algunos nodos con NVMe local, otros respaldados por Ceph. Querían “rendimiento consistente de VM”, así que estandarizaron una plantilla: VirtIO SCSI, iothread habilitado, discard on, y un solo disco virtual grande por simplicidad.
Luego hicieron un cambio ingenioso: habilitaron ballooning agresivo en la flota para mejorar la consolidación. Los gráficos quedaron geniales. El CFO encantado. Las VMs Windows no tanto.
Bajo horas pico, el host reclamó memoria. Windows respondió haciendo paging y perdiendo caches. Eso creó más IO de disco. En nodos respaldados por Ceph, eso significó más escrituras aleatorias pequeñas y más churn de metadata. El IO incrementado elevó la latencia. La latencia mayor ralentizó las aplicaciones, lo que incrementó reintentos de usuarios, lo que elevó la carga. Un bucle de retroalimentación de rendimiento, vendido como “eficiencia de capacidad”.
Intentaron arreglarlo aumentando vCPUs. Eso solo empeoró la contención: más hilos ejecutables compitiendo por el mismo IO y ancho de banda de memoria. Las VMs parecían más ocupadas mientras lograban menos trabajo—un clásico momento de “escalamos el problema”.
La solución eventual fue aburrida y efectiva: desactivar ballooning para servidores de aplicaciones Windows, reservar memoria en el host y separar cargas sensibles a latencia de las ruidosas. La densidad bajó un poco. Los incidentes bajaron mucho. Ese tradeoff se llama “madurez”.
Mini-historia 3: La práctica aburrida pero correcta que salvó el día
Un equipo con el que me gustaba trabajar tenía un ritual: cada vez que desplegaban un nodo Proxmox nuevo o cambiaban almacenamiento, ejecutaban la misma suite de validación. No un circo de benchmarks enorme—solo un puñado de chequeos repetibles: comportamiento de swap del host, await en iostat bajo carga sintética, y una comprobación de sanity de una VM Windows confirmando controladores VirtIO y tipos de controlador.
También mantenían plantillas de VM estrictas. Nadie podía crear Windows con SATA “solo para instalarlo”. La plantilla tenía VirtIO SCSI y la ISO de drivers adjunta; los pasos de build incluían verificar el binding del driver. Era aburrido. Funcionaba.
Un trimestre, una actualización de firmware de almacenamiento introdujo una regresión sutil de latencia. Nada se cayó. Pero las comprobaciones rutinarias del equipo mostraron mayor latencia tail en escrituras aleatorias. Pausaron el despliegue, revirtieron el firmware en los nodos actualizados y evitaron un mes de tickets “los usuarios dicen que está lento” que nunca reproducen cuando estás mirando.
Esto no fue magia. Fue disciplina: una lista corta, una línea base conocida y la humildad de creer que las regresiones de rendimiento son reales aunque nadie señale un cráter humeante.
Listas de verificación / plan paso a paso
Checklist A: Devolver a la cordura una VM Windows lenta existente (enfoque de downtime mínimo)
- Recolecta métricas base: host
iostat -x, hostvmstat, config VM víaqm config. - Verifica controlador y opciones de disco: asegura VirtIO SCSI (o NVMe),
cache=none,iothread=1,discard=ondonde sea soportado. - Instala controladores VirtIO en Windows si no están presentes (almacenamiento + red). Confirma que están activos.
- Desactiva ballooning para cargas sensibles a latencia; fija la memoria.
- Cambia tipo de CPU a host donde las restricciones de migración lo permitan; asegura que el conteo de vCPU sea razonable.
- Vuelve a probar con carga real y verifica que la latencia del host mejore o al menos se estabilice.
Checklist B: Nueva plantilla de VM Windows que no te avergonzará después
- Tipo de máquina: moderno (q35) salvo que necesites compatibilidad legacy.
- Disco: VirtIO SCSI single (o NVMe),
cache=none,iothread=1,discard=on,ssd=1. - Red: VirtIO net.
- CPU: host (o un modelo consistente en el clúster si necesitas migración transparente).
- Memoria: fija; ballooning apagado por defecto para servidores de aplicaciones Windows.
- Instalación: ISO de controladores VirtIO adjunta; confirma drivers antes de terminar el build.
- Post-instalación: agente QEMU guest instalado; plan de energía de Windows adecuado para servidores (evita ahorros agresivos).
Checklist C: Salud del backend de almacenamiento para VMs Windows
- Confirma salud del almacenamiento (sin rebuild/backfill).
- Mide latencia (
iostat -x) durante carga representativa de IO. - ZFS: valida elección de volblocksize antes de provisionar; considera SLOG para cargas con muchas escrituras sync.
- Ceph: vigila ops lentas y jitter de red; evita mezclar cargas críticas en latencia con cargas bulk sin planificar.
- Provisión thin: asegura que discard funcione y monitorea presión de espacio libre.
Preguntas frecuentes
1) ¿Debo usar VirtIO SCSI o NVMe virtual para Windows en Proxmox?
Si quieres un valor seguro: VirtIO SCSI (a menudo virtio-scsi-single) con iothread habilitado. Si persigues alta IOPS con Windows moderno y prefieres semánticas NVMe, NVMe virtual puede ser excelente. Mide en ambos casos; no asumas.
2) ¿Es cache=writeback siempre más rápido?
A menudo parece más rápido hasta que llegan los flushes, y entonces la realidad llega con papeleo. Úsalo solo cuando puedas defender la durabilidad (protección de energía, semánticas de flush correctas y un backend de almacenamiento fiable).
3) ¿Por qué Windows “se congela” cuando el disco está ocupado?
Porque buena parte de la UI y servicios de Windows dependen de la latencia de almacenamiento, no del throughput. Alta latencia tail hace que todo el sistema parezca colgado aunque el promedio MB/s esté bien.
4) ¿Activar discard/TRIM ayuda al rendimiento?
Ayuda a la corrección y estabilidad a largo plazo en sistemas thin y con SSD informando al backend qué está libre. A corto plazo puede añadir trabajo. Si habilitar discard causa picos, aprendiste algo sobre tu backend.
5) ¿Cuántas vCPUs debo dar a una VM Windows?
Las necesarias para la concurrencia de la carga, no tantas como para crear contención de planificación. Empieza más pequeño y escala. Sobredimensionar vCPUs puede empeorar la latencia, especialmente en hosts ocupados.
6) ¿Debo habilitar ballooning para VMs Windows?
Para test/dev o escritorios de bajo riesgo, quizá. Para servidores de aplicaciones Windows en producción donde la gente nota latencia, generalmente no. Memoria fija compra predictibilidad.
7) Mi IO del host se ve bien, pero Windows aún tiene alta longitud de cola de disco. ¿Por qué?
Frecuentemente es un problema de driver/controlador (bus equivocado, VirtIO faltante), o el camino IO de la VM es single-threaded y satura un hilo de QEMU. Revisa tipo de controlador, iothread y binding del driver en Windows.
8) ¿Puede ZFS ser rápido para VMs Windows?
Sí. Pero necesitas respetar escrituras sync y comportamiento de tamaño de bloque. ZFS no fingirá que las escrituras sync son baratas. Si tu carga demanda baja latencia sync, diseña el pool en consecuencia.
9) ¿Cuál es el cambio de mayor impacto para una VM Windows lenta?
Mover de IDE/SATA emulado a VirtIO SCSI (o NVMe) con el controlador de almacenamiento VirtIO adecuado. No es sutil.
10) Si no puedo reinstalar Windows, ¿puedo cambiar de controlador?
Usualmente sí, pero hazlo con cuidado: instala los controladores VirtIO primero mientras el controlador viejo sigue arrancando, luego añade el nuevo controlador/mapeo de disco y finalmente cambia el arranque. Prueba con snapshots/backups.
Conclusión: siguientes pasos que no te arruinarán el fin de semana
Haz que tu VM Windows sea rápida arreglando las partes aburridas: el controlador de disco, el contrato de caché, el threading de IO y los controladores. Luego valida que el host no esté intercambiando y que la latencia del backend de almacenamiento sea compatible con expectativas humanas.
Pasos concretos:
- Ejecuta
qm config <vmid>y confirma VirtIO SCSI/NVMe,cache=none,iothread=1,discard=ondonde sea soportado. - Mide latencia del host con
iostat -x 1durante la lentitud. Si await es feo, deja de tunear el invitado y arregla el almacenamiento. - Desactiva ballooning para la VM (y arregla presión de RAM del host) si ves swapping o comportamiento de reclaim.
- Estandariza una plantilla Windows que incorpore las opciones correctas de controlador/driver para no tener que depurar esto el próximo trimestre.
El trabajo de rendimiento rara vez se trata de trucos geniales. Se trata de eliminar cuellos de botella accidentales y negarse a enviar valores por defecto diseñados para compatibilidad, no para velocidad.