Desplegar una app Flask o Django con Gunicorn y Nginx
Desplegar una aplicación Python web en producción involucra tres piezas que trabajan juntas: la app en sí (Flask o Django), un servidor WSGI que sabe hablar con ella, y un proxy inverso que expone todo al mundo. En este artículo te mostramos cómo encajar las piezas de manera limpia usando Gunicorn y Nginx.
Qué es WSGI y por qué importa
WSGI (Web Server Gateway Interface) es un protocolo estándar que define cómo un servidor web debe pasar las peticiones HTTP a una aplicación Python. Flask y Django exponen objetos compatibles con WSGI, pero no están pensados para atender tráfico real por sí mismos. Necesitas un servidor WSGI de producción entre medio, y el más popular es Gunicorn.
Gunicorn es simple, confiable y corre tu app en múltiples procesos trabajadores (workers). Cada worker maneja peticiones de forma independiente, así que si uno se cuelga, los demás siguen funcionando.
Preparar el entorno
Dentro del entorno virtual de tu proyecto, instala Gunicorn:
source venv/bin/activate pip install gunicorn
Prueba que arranque manualmente. Para Flask, suponiendo que tu archivo se llama app.py y el objeto se llama app:
gunicorn --workers 3 --bind 127.0.0.1:8000 app:app
Para Django, donde tu proyecto se llama misitio:
gunicorn --workers 3 --bind 127.0.0.1:8000 misitio.wsgi:application
Si todo arranca sin errores, presiona Ctrl+C y pasa al siguiente paso.
Crear un servicio systemd
No quieres lanzar Gunicorn a mano cada vez. Crea /etc/systemd/system/miapp.service con este contenido:
[Unit] Description=Gunicorn para miapp After=network.target [Service] User=www-data Group=www-data WorkingDirectory=/var/www/miapp Environment="PATH=/var/www/miapp/venv/bin" ExecStart=/var/www/miapp/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 app:app Restart=always [Install] WantedBy=multi-user.target
Actívalo con:
sudo systemctl daemon-reload sudo systemctl enable --now miapp
A partir de este punto, tu app se mantiene viva incluso si el servidor reinicia, y si Gunicorn muere por algún motivo, systemd lo levanta solito.
Configurar Nginx al frente
Crea el server block en /etc/nginx/sites-available/miapp:
server {
listen 80;
server_name miapp.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /static/ {
alias /var/www/miapp/staticfiles/;
expires 30d;
}
location /media/ {
alias /var/www/miapp/media/;
}
}
Fíjate que los archivos estáticos se sirven directamente desde Nginx, sin pasar por Gunicorn. Esto es clave en Django: después de cada deploy debes correr python manage.py collectstatic para juntar todos los estáticos en la carpeta que configuraste.
Activa el sitio y recarga:
sudo ln -s /etc/nginx/sites-available/miapp /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx
Revisar errores
Cuando algo no funcione, revisa en este orden:
sudo journalctl -u miapp -f sudo tail -f /var/log/nginx/error.log
El primer comando muestra los logs de Gunicorn (donde aparecen las excepciones de Python), y el segundo los de Nginx (errores de proxy, permisos, etc.). La mayoría de los problemas de despliegue se resuelven revisando estos dos archivos.