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) + 1y dónde está mal - Por qué importa
--max-requests(creep lento de memoria, fragmentación de GC) --timeoutvs--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-configpara verificar qué se carga realmente
Entregamos esta configuración en cada servicio Python gestionado.
Artículo completo disponible
Leer el artículo completo