Descargar modelos LLM en local es algo que se hace de vez en cuando, pero cuando te bajas un modelo masivo de 122B parámetros como Sehyo/Qwen3.5-122B-A10B-NVFP4, la descarga copa toda la conexión y deja al resto de la casa sin red. El CLI de Hugging Face (huggingface-cli o hf) no tiene un flag --limit-rate, así que toca buscar alternativas. En este apunte explico dos formas de limitar el ancho de banda en Linux usando Docker (mi preferida) o Wondershaper a nivel de host.
El problema
Tengo una conexión de fibra de 1 Gbps en casa. Cuando lanzo una descarga masiva desde Hugging Face, el CLI intenta usar todo el ancho de banda disponible. Eso significa que mientras se descarga un modelo de más de 70GB, nadie más en casa puede navegar o trabajar con normalidad.
Lo lógico sería que huggingface-cli tuviese un flag tipo --limit-rate como tiene curl, así que la solución pasa por limitar el ancho de banda a nivel de red, fuera de la herramienta.
Método 1: Docker (recomendado)
La idea es levantar un contenedor Docker para la descarga y limitar su tráfico de red con tc (Traffic Control de Linux). De esta forma la conexión del host queda intacta y el resto de dispositivos no se ven afectados.
Por qué tc desde el host y no desde dentro del contenedor
El primer impulso es ejecutar tc dentro del propio contenedor, aplicando un Token Bucket Filter (TBF) en su eth0. El problema es que tc en modo root qdisc solo limita el tráfico de salida (egress). Las descargas son tráfico de entrada (ingress), así que el límite no les afecta. Probé también un ingress policer dentro del contenedor, pero TCP compensa los paquetes descartados y la limitación real es mínima.
La solución que funciona es aplicar tc desde el host en la interfaz veth que Docker crea para el contenedor. Desde la perspectiva del host, el tráfico que envía hacia el contenedor es egress, y ahí sí funciona el TBF perfectamente.
El script hf-download-limited.sh
He creado un script que automatiza todo el proceso:
hf-download-limited.sh
#!/usr/bin/env bash
# hf-download-limited.sh
#
# Descarga un modelo de Hugging Face con ancho de banda limitado.
# Usa Docker + tc aplicado desde el host en la interfaz veth del contenedor.
#
# Requiere: docker, sudo (para tc), iproute2 en el host
set -euo pipefail
CONTAINER="hf-dl-$$"
LATENCY="400ms"
# ─── Ayuda ────────────────────────────────────────────────────
usage() {
cat <<'EOF'
Uso: hf-download-limited.sh -m MODEL -d DIR [-b BANDWIDTH]
Descarga un modelo de Hugging Face limitando el ancho de banda
mediante Docker + tc (Traffic Control).
Argumentos obligatorios:
-m, --model MODEL Modelo de Hugging Face (ej: usuario/modelo)
-d, --dir DIR Directorio local donde guardar el modelo
Argumentos opcionales:
-b, --bandwidth MBIT Ancho de banda máximo en Mbps (default: 600)
-t, --token TOKEN Token de Hugging Face (o usa env HF_TOKEN)
-h, --help Muestra esta ayuda
Ejemplo:
./hf-download-limited.sh \
-m "Sehyo/Qwen3.5-122B-A10B-NVFP4" \
-d "/home/luis/tmp/Qwen3.5-122B-A10B-NVFP4" \
-b 600
EOF
exit "${1:-0}"
}
# ─── Parseo de argumentos ────────────────────────────────────
MODEL=""
LOCAL_DIR=""
RATE_MBIT=600
while [[ $# -gt 0 ]]; do
case "$1" in
-m|--model) MODEL="$2"; shift 2 ;;
-d|--dir) LOCAL_DIR="$2"; shift 2 ;;
-b|--bandwidth) RATE_MBIT="$2"; shift 2 ;;
-t|--token) HF_TOKEN="$2"; shift 2 ;;
-h|--help) usage 0 ;;
*) echo "Error: argumento desconocido '$1'"; usage 1 ;;
esac
done
if [[ -z "$MODEL" || -z "$LOCAL_DIR" ]]; then
echo "Error: -m MODEL y -d DIR son obligatorios."
echo ""
usage 1
fi
# ─── Calcular parámetros de tc ────────────────────────────────
# burst = 2% del rate, mínimo 1 mbit (suficiente para kernels con HZ=250..1000)
RATE="${RATE_MBIT}mbit"
BURST_MBIT=$(( RATE_MBIT / 50 ))
if (( BURST_MBIT < 1 )); then
BURST_MBIT=1
fi
BURST="${BURST_MBIT}mbit"
# ─── Limpieza al salir (Ctrl+C, error, etc.) ─────────────────
cleanup() {
echo ""
echo "--- Limpiando ---"
docker stop "$CONTAINER" 2>/dev/null || true
echo "Contenedor eliminado."
}
trap cleanup EXIT
# 1. Lanzar contenedor en segundo plano
echo "--- Iniciando contenedor ---"
HF_TOKEN_ARGS=()
if [[ -n "${HF_TOKEN:-}" ]]; then
HF_TOKEN_ARGS=(-e "HF_TOKEN=$HF_TOKEN")
fi
docker run -d --rm --name "$CONTAINER" \
-v "$(dirname "$LOCAL_DIR"):$(dirname "$LOCAL_DIR")" \
-e DEBIAN_FRONTEND=noninteractive \
"${HF_TOKEN_ARGS[@]}" \
python:3.10-slim \
sleep infinity > /dev/null
# 2. Instalar huggingface_hub dentro del contenedor
echo "--- Instalando huggingface_hub ---"
docker exec "$CONTAINER" pip install -qqq -U huggingface_hub
# 3. Encontrar la interfaz veth en el host
IFLINK=$(docker exec "$CONTAINER" cat /sys/class/net/eth0/iflink)
VETH=$(ip -o link | awk -v idx="$IFLINK" '$1 == idx":" {print $2}' | sed 's/@.*//')
if [[ -z "$VETH" ]]; then
VETH=$(ip -o link | grep "^${IFLINK}: " | awk '{print $2}' | sed 's/@.*//' | sed 's/://')
fi
if [[ -z "$VETH" ]]; then
echo "ERROR: No se encontró la interfaz veth para iflink=$IFLINK"
exit 1
fi
# 4. Aplicar tc (Token Bucket Filter) en la veth del host
echo "--- Aplicando tc en $VETH: rate=$RATE burst=$BURST ---"
sudo tc qdisc add dev "$VETH" root tbf \
rate "$RATE" burst "$BURST" latency "$LATENCY"
# 5. Lanzar la descarga (-it para barra de progreso)
echo ""
echo "=== Descargando $MODEL ==="
echo "=== Destino: $LOCAL_DIR ==="
echo "=== Límite: ${RATE_MBIT} Mbps ==="
echo ""
docker exec -it -e "PYTHONWARNINGS=ignore::UserWarning" "${HF_TOKEN_ARGS[@]}" "$CONTAINER" \
hf download "$MODEL" --local-dir "$LOCAL_DIR"
echo ""
echo "=== Descarga completada ==="
Ejecútalo pasándole el modelo y el directorio de destino:
chmod +x hf-download-limited.sh
./hf-download-limited.sh \
-m "Sehyo/Qwen3.5-122B-A10B-NVFP4" \
-d "/home/luis/tmp/Qwen3.5-122B-A10B-NVFP4" \
-b 600
El flag -b permite ajustar el límite de ancho de banda en Mbps (por defecto 600). Si tienes un token de Hugging Face puedes pasarlo con -t para obtener descargas más rápidas y rate limits más altos.
Qué hace el script paso a paso
- Parsea los argumentos (
-mmodelo,-ddirectorio,-bbandwidth,-ttoken) y calcula automáticamente elburstdel Token Bucket Filter a partir del bandwidth. - Lanza un contenedor con
sleep infinityen segundo plano e instalahuggingface_hubdentro. - Busca la interfaz veth en el host. Cada contenedor Docker tiene una interfaz
eth0interna que está conectada a unavethen el host. El script lee eliflinkdel contenedor y busca la interfaz correspondiente conip -o link. - Aplica
tcdesde el host en esaveth. Un Token Bucket Filter (TBF) limita el tráfico que el host envía al contenedor, es decir, las descargas. - Ejecuta
hf downloaddentro del contenedor con-itpara ver la barra de progreso. - Limpia al terminar. Un
trap cleanup EXITse encarga de parar el contenedor si la descarga termina, falla, o haces Ctrl+C.
La salida es limpia:
--- Iniciando contenedor ---
--- Instalando huggingface_hub ---
--- Aplicando tc en vethe4845c6: rate=600mbit burst=12mbit ---
=== Descargando Sehyo/Qwen3.5-122B-A10B-NVFP4 ===
=== Destino: /home/luis/tmp/Qwen3.5-122B-A10B-NVFP4 ===
=== Límite: 600 Mbps ===
Fetching 15 files: 47%|████▋ | 7/15 [02:30<03:00, ...]
Cuando la descarga termina, el contenedor desaparece y con él todas las restricciones de red. El sistema queda limpio sin necesidad de hacer nada más.

Método 2: Wondershaper en el host
Si prefieres no usar Docker, puedes limitar el ancho de banda directamente en la interfaz de red del host.
Ojo con el Wondershaper de apt
wondershaper del repositorio de Ubuntu, está completamente obsoleto y usa módulos del kernel deprecados (cbq), lo que provoca errores RTNETLINK. Usa el fork moderno de GitHub.Instalar el fork moderno
El fork mantenido de magnific0/wondershaper usa el módulo htb (Hierarchical Token Bucket), que es el estándar actual.
git clone https://github.com/magnific0/wondershaper.git
cd wondershaper
Aplicar el límite
Primero identifico mi interfaz de red:
ip route | grep default
En mi caso es enP7s7. El script usa Kilobits por segundo (Kbps). Para limitar la descarga a unos 75 MB/s (600.000 Kbps) y la subida a 40 MB/s (320.000 Kbps):
sudo ./wondershaper -a enP7s7 -d 600000 -u 320000
Descargar el modelo
Con la red del host limitada, lanzo la descarga:
hf download Sehyo/Qwen3.5-122B-A10B-NVFP4 \
--local-dir /mnt/baul/models/Qwen3.5-122B-A10B-NVFP
Quitar el límite
No te olvides de este paso
sudo ./wondershaper -c -a enP7s7
Conclusión
Si tienes Docker disponible, el método 1 es claramente mejor: limita solo el tráfico del contenedor, no necesitas tocar la red del host y se limpia solo al terminar. El método 2 con Wondershaper es una buena alternativa si no puedes o no quieres usar Docker, pero ten en cuenta que afecta a toda la máquina y tienes que acordarte de quitar el límite cuando termines.
Enlaces interesantes
- Hugging Face CLI - Documentación oficial del CLI
- magnific0/wondershaper - Fork moderno de Wondershaper
- tc - Traffic Control - Manual de
tcen Linux - Token Bucket Filter (TBF) - Documentación sobre TBF