Saltar al contenido
EdgeServers
Blog

Gunicorn y Uvicorn en producción — el tuning de workers que de verdad aplicamos

12 de mayo de 2026 · 1 min de lectura · por Sudhanshu K.

Los servicios web Python en 2026 se dividen en dos campos: WSGI tradicional (Django, Flask) servido vía workers sync de Gunicorn, y ASGI moderno (FastAPI, Starlette, Django 5 async) servido vía workers Uvicorn bajo un master Gunicorn. Ambos pueden ser de calidad de producción. Ninguno funciona bien en valores por defecto.

La mala configuración más común que vemos: 2 × CPU + 1 workers corriendo contra una app ASGI async que debería ser un worker Uvicorn por core sin multiplicador de concurrencia. La CPU queda ociosa porque cada worker es de un solo hilo y el async corre en un único event loop.

El patrón Gunicorn + Uvicorn para FastAPI

gunicorn app.main:app \
  --workers $(( $(nproc) )) \
  --worker-class uvicorn.workers.UvicornWorker \
  --bind 0.0.0.0:8000 \
  --timeout 30 \
  --graceful-timeout 30 \
  --keep-alive 5 \
  --max-requests 10000 \
  --max-requests-jitter 1000 \
  --access-logfile -

Un worker Uvicorn por core. Cada worker corre un event loop de un solo hilo. La concurrencia async viene de esperar I/O, no del threading.

El artículo completo cubre:

  • La fórmula sync-worker (2 × CPU) + 1 y dónde está mal
  • Por qué importa --max-requests (creep lento de memoria, fragmentación de GC)
  • --timeout vs --graceful-timeout — y el orden en que disparan
  • Cuándo gthread (sync con hilos) es la elección correcta
  • ProxyHeadersMiddleware para la cadena X-Forwarded-For
  • Leer gunicorn --print-config para verificar qué se carga realmente

Entregamos esta configuración en cada servicio Python gestionado.

Artículo completo disponible

Leer el artículo completo