TL;DR: PyTorch 2.11 permite instalar wheels de PyTorch con CUDA habilitado en Linux aarch64 directamente desde PyPI, eliminando los índices personalizados y los workarounds que complicaban el despliegue en sistemas como NVIDIA GH200, GB200* y GB300. Kaichao You (Inferact) explica cómo este cambio de empaquetado mejora la experiencia de instalación para usuarios de vLLM y muestra cómo la colaboración entre vLLM y PyTorch a través de la PyTorch Foundation ayudó a llevar el fix a producción.*

Un fix de dos años en el horno que hace mucho más simple la vida en GB200, GB300 y GH200.

¿Qué problema se soluciona en PyTorch 2.11?

La historia arranca en octubre de 2024. En el hackathon presencial de CUDA MODE (hoy GPU MODE), Kaichao intentaba levantar vLLM en una caja GH200. Debió haber sido una pega de cinco minutos. Pasó horas mirando un pip install que se veía perfecto: los wheels se resolvían, las dependencias se satisfacían, la instalación terminaba sin errores. Pero en runtime, torch.cuda.is_available() devolvía obstinadamente False.

La razón, una vez excavada, era casi cómicamente trivial: en Linux aarch64, pip install torch traía el wheel CPU-only desde PyPI. Simplemente no había un wheel con GPU para aarch64 publicado en el índice por defecto. Para obtener un build con CUDA habilitado había que apuntar pip explícitamente al índice de descargas de PyTorch.

Código
pip install torch --index-url https://download.pytorch.org/whl/cu128

Eso, por sí solo, sería apenas una molestia. El daño real venía por cómo esto interactuaba con las dependencias transitivas. PyPI no permite que un paquete especifique un índice personalizado para sus dependencias. Si algún paquete del árbol de vLLM declaraba un requirement de torch==<alguna_versión>, y esa versión no calzaba, pip volvía felizmente al índice por defecto de PyPI, encontraba el wheel CPU, desinstalaba silenciosamente el build con GPU que recién instalaste y lo reemplazaba por el CPU. Uno creía que todo andaba bien hasta que el modelo se negaba a encontrar la GPU.

Para cualquiera que intentara levantar vLLM en GH200, y después en GB200 y GB300, esto convertía una instalación de una línea en un laberinto de flags --index-url, versiones pineadas y chequeos de cordura post-instalación.

¿Qué workarounds cargaba vLLM mientras tanto?

Mientras se esperaba el fix upstream, vLLM tuvo que enviar sus propios workarounds para que los usuarios de aarch64 no quedaran trabados.

El primero fue use_existing_torch.py, agregado en vllm-project/vllm#8713 en septiembre de 2024. El PR lo planteó sin rodeos: "habilitar PyTorch existente (para GH200, aarch64, nightly)". El flujo es lo que el nombre sugiere: el usuario instala el torch correcto por su cuenta (desde el índice de PyTorch, un nightly o un build custom), luego ejecuta python use_existing_torch.py, que arranca toda mención de torch, torchvision y torchaudio de los requirements/*.txt, requirements/*.in y pyproject.toml de vLLM. Sin esos pines, el siguiente install de vLLM ya no puede gatillar que pip "ayude" volviendo al índice por defecto y cambiando silenciosamente tu torch con CUDA por el CPU. Es feo (literalmente reescribimos nuestros propios archivos de dependencias al momento de instalar), pero mantuvo desbloqueados a los usuarios GH200 por más de un año.

Después, a medida que uv maduró, llegó una opción más limpia. En vllm-project/vllm#24303 se sumó al pyproject.toml:

Código
[tool.uv]
no-build-isolation-package = ["torch"]

Esto le indica a uv que no construya torch en un entorno aislado. En la práctica, significa que uv reutilizará el torch ya presente en el entorno actual en lugar de tratar de resolver y reinstalar su propia copia. Combinado con instalar torch primero desde el índice correcto, esto dio un camino mucho más ergonómico que la maniobra de reescribir archivos: una sola línea de config en pyproject.toml, y uv pip install vllm (o un uv sync) respetaba el torch con CUDA preinstalado en aarch64.

El workaround de vLLM es la comunidad improvisando alrededor de un hueco en el estándar de empaquetado. Wheel Variants es NVIDIA y Astral formalizando el fix para que la improvisación deje de ser necesaria.

Del dolor de cabeza al comité técnico de la PyTorch Foundation

Avanzando rápido a 2025. vLLM se sumó a la PyTorch Foundation, y Kaichao pasó a ser uno de sus representantes en el Technical Advisory Committee (TAC). La situación del wheel aarch64 seguía apareciendo, en su propio trabajo y en reportes de otros usuarios de vLLM en sistemas Grace Hopper y Grace Blackwell. En agosto de 2025 abrió pytorch/pytorch#160162 para trackear el problema formalmente, y este año, en una reunión del TAC en enero de 2026, lo planteó directamente en nombre de los usuarios de vLLM.

El pedido era simple: publicar wheels GPU de aarch64 en el índice por defecto de PyPI para que pip install torch "simplemente funcionara" en máquinas clase GB200, igual que en x86. Esos wheels enlazarían dinámicamente a librerías como NCCL y cuBLAS, el mismo enfoque que ya usaba x86, así no se inflan en tamaño. Los binarios grandes son difíciles de descargar para los usuarios y caros de hostear para los mantenedores de PyPI. De ahí que el tamaño grande esté limitado y fuertemente desincentivado por los mantenedores de PyPI.

El equipo de ingeniería de NVIDIA solicitó que los wheels CUDA SBSA se publicaran en PyPI, y luego impulsó el enfoque de wheels pequeños que enlazan contra ellos.

Este es exactamente el tipo de problema cross-project a nivel infraestructura donde la PyTorch Foundation está bien posicionada para coordinar. vLLM y PyTorch son ambos proyectos de la Foundation, y tener un foro compartido para sacar a la luz la fricción del ecosistema (en lugar de que cada proyecto trabaje sus propios workarounds en aislamiento) terminó haciendo una diferencia real.

¿Cuándo aterrizó el fix y cómo se verifica?

En abril de 2026, en otra reunión del TAC, llegó la noticia: el problema está resuelto. Desde PyTorch 2.11.0, el pip install torch por defecto en Linux aarch64 ahora trae un wheel con CUDA habilitado en lugar del CPU-only. Piotr Bialecki, de NVIDIA, confirmó que el cambio está vivo en la release 2.11.0.

Kaichao lo verificó en una GB200 y la diferencia es exactamente la que uno querría: aburrida, en el mejor sentido posible.

Código
$ uv run --no-project --python 3.12 --with 'torch==2.11.0' -- python -c "import torch; print(torch.cuda.is_available())"
True

$ uv run --no-project --python 3.12 --with 'torch==2.10.0' -- python -c "import torch; print(torch.cuda.is_available())"
False

Un bump de versión y toda la pila de workarounds desaparece. Sin más URLs custom de índice propagándose por archivos de requirements. Sin reemplazos silenciosos del wheel CPU pisando un install que funcionaba. Sin sesiones de debugging del estilo "por qué mi GB200 no encuentra la GPU" para usuarios nuevos.

Para vLLM en particular, esto significa que instalar en GB200 y GB300 es ahora genuinamente fluido. Los usuarios nuevos que llegan con un sistema Grace Blackwell pueden seguir las instrucciones estándar y que las cosas funcionen a la primera, algo que importa bastante cuando se está tratando de levantar inferencia sobre una plataforma recién horneada.

Los workarounds en vLLM (tanto use_existing_torch.py como la opción [tool.uv] no-build-isolation-package = ["torch"]) se quedan. Siguen siendo útiles para usuarios avanzados que corren un build custom de PyTorch (un nightly, un fork con parches, o un build desde fuente acoplado a un build de vLLM desde fuente) y necesitan que el install de vLLM deje ese torch estrictamente intacto. Lo que cambia es el camino por defecto: los usuarios comunes en aarch64 ya no tienen que saber que nada de esto existe. Pueden hacer pip install y seguir trabajando, y los workarounds quedan en silencio como herramienta de usuario avanzado y no como impuesto sobre todos.

¿Por qué importa este cambio?

Es un cambio pequeño dentro del esquema general. Es un retoque de empaquetado, no una feature nueva. Pero conviene apreciarlo por un par de razones.

Primero, es un ejemplo concreto de vLLM y PyTorch colaborando productivamente bajo el paraguas de la PyTorch Foundation. El TAC no es solo un ritual de gobernanza; es el lugar donde los dolores de proyectos downstream pueden aterrizar frente a la gente que efectivamente los puede arreglar, y donde la coordinación entre proyectos pasa por default y no por accidente. Este problema recorrió el camino completo: desde un desarrollador puteando contra una terminal en un hackathon, hasta una discusión en el TAC, hasta un issue trackeado en GitHub, hasta una release. Y la Foundation fue lo que hizo corto ese camino.

Segundo, la experiencia de desarrollo se compone. Cada hora que alguien no gasta peleando con flags --index-url es una hora que pasa construyendo cosas reales sobre vLLM y PyTorch. Los sistemas GPU aarch64 solo se van a volver más comunes, y conviene arreglar esto ahora, en la capa aburrida de infraestructura, antes que dejar a cada usuario que lo descubra y lo trabaje por su cuenta.

El workaround del lado de uv (build isolation passthrough) es parte del esfuerzo más amplio de WheelNext, un empujón muy bienvenido para repensar cómo el empaquetado de Python maneja dependencias atadas a aceleradores en la era de IA.

Un agradecimiento grande para quienes hicieron posible este cambio: Alban Desmaison, Nikita Shulga y Andrey Talman del core de PyTorch, que tomaron el pedido original y ayudaron a moverlo; el equipo PyTorch de NVIDIA, que empujó el trabajo de builds aarch64 y confirmó que el fix había aterrizado en 2.11.0, con Piotr Bialecki sosteniendo el esfuerzo y siendo el punto de contacto estable entre NVIDIA y upstream; el equipo de release engineering de PyTorch por construir y publicar los wheels; y los muchos ingenieros tras bambalinas a través de PyTorch, NVIDIA y Arm, cuyo trabajo en toolchains, infraestructura de CI y empaquetado lo hizo posible. Gracias también al TAC por mantener la puerta abierta a este tipo de conversaciones.