Self-hosting de LLMs: guía práctica con llama.cpp y vLLM
TL;DR
- llama.cpp para uso local y experimentación. Una línea para arrancar.
- vLLM para serving en producción con múltiples requests concurrentes.
- La decisión depende de si eres tú solo o necesitas servir a usuarios.
¿Por qué self-hosting?
Tres razones reales (no marketing):
- Privacidad: Tus datos no salen de tu máquina. Cero risk de data leaks.
- Coste: Si procesas >5M tokens/día, una GPU propia es más barata que cualquier API.
- Latencia: Sin red de por medio. 50-200ms por token en hardware decente.
Hardware necesario
La pregunta que todo el mundo hace primero:
| Modelo | Parámetros | VRAM mínima (Q4) | VRAM recomendada | GPU ejemplo |
|---|---|---|---|---|
| Llama 4 Scout 17B | 17B | 10 GB | 12 GB | RTX 4070 |
| Qwen 3 30B | 30B | 18 GB | 24 GB | RTX 4090 |
| DeepSeek V4 | 685B MoE | 40 GB (Q4, partial) | 80 GB | A100 80GB |
| Llama 4 Maverick 400B | 400B MoE | 24 GB (Q4, partial) | 48 GB | A6000 |
Para Mac: Apple Silicon con memoria unificada. Un M4 Max con 128GB puede correr modelos de 70B en Q4 sin problema.
Regla práctica: VRAM necesaria ≈ parámetros × 0.6 (Q4_K_M).
llama.cpp: uso local
Instalación
# macOS
brew install llama.cpp
# Linux
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp && make -j
Descargar modelo
Desde HuggingFace en formato GGUF:
# Descargar Llama 4 Scout cuantizado
huggingface-cli download bartowski/Llama-4-Scout-17B-16E-GGUF \
Llama-4-Scout-17B-16E-Q4_K_M.gguf \
--local-dir ./models
Ejecutar
# Chat interactivo
llama-cli -m models/Llama-4-Scout-17B-16E-Q4_K_M.gguf \
-ngl 99 \
--color \
-i -cnv
# Servidor OpenAI-compatible
llama-server -m models/Llama-4-Scout-17B-16E-Q4_K_M.gguf \
-ngl 99 \
--host 0.0.0.0 \
--port 8080
El flag -ngl 99 descarga todas las capas a GPU. Sin esto, corre en CPU (lento).
Cuantización
El formato GGUF soporta varios niveles:
| Cuantización | Tamaño modelo | Calidad relativa | Cuándo usar |
|---|---|---|---|
| Q8_0 | 100% | ~99% | Máxima calidad, suficiente VRAM |
| Q5_K_M | ~65% | ~97% | Buen balance |
| Q4_K_M | ~55% | ~95% | VRAM limitada, estándar |
| Q3_K_M | ~45% | ~90% | VRAM muy limitada |
| Q2_K | ~35% | ~85% | Emergencias, calidad baja |
Recomendación: Q4_K_M para la mayoría de casos. La pérdida de calidad es mínima y ahorras casi la mitad de VRAM.
vLLM: serving en producción
Instalación
pip install vllm
Requiere GPU NVIDIA con CUDA. No funciona en Mac (usa Metal en Mac con llama.cpp).
Servidor OpenAI-compatible
vllm serve meta-llama/Llama-4-Scout-17B-16E \
--tensor-parallel-size 1 \
--max-model-len 8192 \
--port 8000
Esto arranca un servidor compatible con la API de OpenAI:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")
response = client.chat.completions.create(
model="meta-llama/Llama-4-Scout-17B-16E",
messages=[{"role": "user", "content": "Explica transformers en 3 frases"}],
)
print(response.choices[0].message.content)
Continuous batching
La ventaja real de vLLM: maneja múltiples requests concurrentes eficientemente con continuous batching. A diferencia de llama.cpp (que procesa de uno en uno), vLLM agrupa requests y las procesa en paralelo.
Throughput comparado (Llama 4 Scout, 1× A100):
| Servidor | Tokens/sec (1 request) | Tokens/sec (10 concurrent) |
|---|---|---|
| llama.cpp | 45 | 45 (secuencial) |
| vLLM | 40 | 350+ (parallel) |
Para uso individual, llama.cpp es suficiente. Para servir usuarios, vLLM es 8x más eficiente.
Configuración de producción
vllm serve meta-llama/Llama-4-Scout-17B-16E \
--tensor-parallel-size 2 \
--max-model-len 4096 \
--gpu-memory-utilization 0.9 \
--swap-space 4 \
--port 8000 \
--enable-prefix-caching
--tensor-parallel-size 2: usa 2 GPUs.--gpu-memory-utilization 0.9: usa 90% de VRAM.--enable-prefix-caching: cachea system prompts compartidos (ahorra tokens).
Coste: self-hosting vs API
| Setup | Coste mensual | Tokens/día máx | Cuándo compensa |
|---|---|---|---|
| RTX 4070 (12GB) | ~$50 (electricidad + amortización) | ~5M | >2M tokens/día |
| RTX 4090 (24GB) | ~$120 | ~20M | >8M tokens/día |
| A100 80GB (cloud) | ~$1,500 (Lambda/RunPod) | ~100M | >50M tokens/día |
| OpenAI API | Variable | Ilimitado | <2M tokens/día |
Regla: Si procesas menos de 2M tokens/día, usa API. Es más barato y no gestionas infra.
Cuándo usar cada uno
llama.cpp:
- Desarrollo local
- Experimentación
- Mac (Apple Silicon)
- Un solo usuario
- No necesitas alta concurrencia
vLLM:
- Producción
- Múltiples usuarios concurrentes
- Servidores con GPUs NVIDIA
- Necesitas API compatible con OpenAI
- Throughput importa
API (OpenAI/Anthropic/Together):
- <2M tokens/día
- No quieres gestionar hardware
- Necesitas el mejor modelo disponible
- Time-to-market importa más que coste
Problemas comunes
OOM (Out of Memory): Reduce --max-model-len o usa cuantización más agresiva (Q3 en lugar de Q4).
Latencia alta en vLLM: Aumenta --gpu-memory-utilization a 0.95 y activa prefix caching.
Modelo no encontrado: vLLM descarga automáticamente de HuggingFace. Necesitas huggingface-cli login para modelos gated (Llama, etc.).
Calidad pobre: La cuantización Q2_K degrada calidad significativamente. Usa Q4_K_M como mínimo para producción.
Fuentes: llama.cpp docs, vLLM docs, HuggingFace, benchmarks propios en RTX 4090 y A100.