Desplegar una app Flask o Django con Gunicorn y Nginx
Dedicados & VPS
2

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.