COLOCA EN INTERNET PARA TODO EL MUNDO TU APP WEB HECHA EN PYTHON

Deployment de una app web Python en AWS EC2. Flask + Gunicorn + systemd + Nginx

Guía para desplegar una aplicación web construida con Python + Flask, incluyendo casos con Dash como framework de visualización en una instancia AWS EC2 con Ubuntu Server, usando una arquitectura estándar de producción:

  • Flask: como app WSGI.
  • Gunicorn: como servidor WSGI.
  • systemd: como gestor de proceso.
  • Nginx: como reverse proxy.

También incluye una sección de diagnóstico de errores reales como 500/502/504, timeouts, OOM-kill y una checklist final para que puedas repetir el proceso con consistencia.

¿Qué es AWS EC2? Ventajas y alternativas

Amazon EC2 (Elastic Compute Cloud) es un servicio de AWS que te permite crear y administrar servidores virtuales en la nube (instancias). En la práctica, EC2 es un “Linux/Windows remoto” donde tú controlas casi todo: sistema operativo, paquetes, firewall, procesos, configuración de Nginx, etc.

Ventajas principales

  • Control total del sistema: puedes instalar lo que quieras (PostgreSQL, Redis, Nginx, librerías del sistema, etc.).
  • Escalabilidad: puedes cambiar el tipo de instancia, agregar discos, balanceadores, auto-scaling, etc.
  • Ecosistema AWS: integración natural con Route 53 (DNS), IAM (permisos), S3 (archivos), RDS (bases de datos), CloudWatch (logs), etc.
  • Costos flexibles: pagas por sólo el uso y hay disponibles capas free tier como instancias pequeñas para empezar un proyecto.

Desventajas o costos ocultos de EC2

  • Más responsabilidad operativa: tú configuras todo (seguridad, actualizaciones, backups, hardening, etc.).
  • No es serverless: requiere mantenimiento (parches, reinicios, monitoreo).
  • Riesgo de errores de configuración: un security group mal definido o una mala configuración de Nginx puede bloquear tu app o exponer tu servidor.

Alternativas dentro y fuera de AWS

Dependiendo de tu objetivo y si prefieres rapidez o control puedes considerar:

  • AWS Lightsail: más simple que EC2, ideal para primeros despliegues; menos flexible pero más directo.
  • AWS Elastic Beanstalk: PaaS que automatiza despliegue, scaling y monitoreo; tú subes el código y AWS maneja gran parte de la operación.
  • AWS ECS/Fargate: contenedores en producción; excelente para escalado y deployments reproducibles con Docker.
  • AWS App Runner: despliegue administrado de servicios web (contenedores o repositorios), con menos fricción que ECS.
  • Heroku / Render / Railway: experiencias PaaS muy simples, buena opción para proyectos personales/portafolio.
  • DigitalOcean Droplets: similar a EC2, interfaz más simple.
  • Google Compute Engine / Azure Virtual Machines: equivalentes a EC2 en otras nubes.

¿Qué es Flask brevemente?

Flask es un microframework web de Python (ligero y flexible) para crear aplicaciones web y APIs. Se caracteriza por:

  • Un núcleo pequeño y extensible donde tú eliges librerías para auth, ORM, etc.
  • Enfoque claro en rutas, vistas y plantillas con Jinja2.
  • Ideal para aplicaciones web de tamaño pequeño a mediano, APIs, prototipos y proyectos que crecen con el tiempo.

En producción, Flask normalmente se ejecuta con un servidor WSGI como Gunicorn o uWSGI, y se coloca detrás de un reverse proxy como Nginx.

Arquitectura recomendada para producción

Una arquitectura típica y estable para el futuro es:

  • El usuario entra por HTTP/HTTPS al servidor.
  • Nginx recibe la petición y actúa como reverse proxy.
  • Nginx reenvía la petición a Gunicorn (por socket UNIX o TCP).
  • Gunicorn ejecuta tu app Flask/Dash y devuelve la respuesta a Nginx.
  • Nginx entrega la respuesta final al navegador.

Ventajas de Nginx delante de Gunicorn:

  • Maneja HTTPS (TLS) y certificados con buena performance.
  • Gestiona timeouts, buffers y tamaño máximo de carga (payload).
  • Puede servir archivos estáticos eficientemente (CSS/JS/imagenes).
  • Evita exponer directamente Gunicorn a Internet.

Requisitos previos

Antes de iniciar, asegúrate de tener:

  1. Una cuenta de AWS con permisos para crear instancias EC2 y security groups.
  2. Un Key Pair (.pem) para autenticación SSH.
  3. Conocimientos básicos de terminal/SSH.
  4. Un repositorio con tu aplicación:
    • Un punto de entrada claro como main.py o app.py.
    • Un objeto WSGI exportado como app, por ejemplo app = Flask(__name__).
    • Un archivo requirements.txt actualizado.

Convención utilizada en el artículo:

  • Usuario remoto: ubuntu.
  • Ruta del proyecto: /home/ubuntu/APP.
  • Módulo y app WSGI: main:app.
  • Puerto interno de Gunicorn: 127.0.0.1:8000 (usado para pruebas).
  • Socket UNIX final: /home/ubuntu/APP/flaskapp.sock.

Paso a paso del deployment en EC2

1. Crear la instancia EC2.

  • En AWS Console: EC2 → Launch instance.
  • Selecciona una AMI: Ubuntu Server 22.04 LTS (recomendado debido a la estabilidad).
  • Elige un tipo de instancia, por ejemplo t2.micro, t3.micro, etc.
  • Crea o selecciona un Key Pair. Descarga el .pem y guárdalo en un lugar seguro.
  • Configura un Security Group (reglas de entrada). Como mínimo:
  • Texto
    SSH (22)    TCP   My IP/32
    HTTP (80)   TCP   0.0.0.0/0
    HTTPS (443) TCP   0.0.0.0/0
  • Lanza la instancia y espera a que esté en estado running.
  • Copia su Public IPv4 address.

2. Conectar por SSH.

En tu máquina local:

  • Ajusta permisos del .pem:
  • Terminal
    chmod 400 ~/Downloads/mi-llave.pem
  • Conéctate:
  • Terminal
    ssh -i ~/Downloads/mi-llave.pem ubuntu@IP_PUBLICA

3. Actualizar el sistema.

En la instancia:

Terminal
sudo apt update && sudo apt upgrade -y

Si actualizas kernel o servicios críticos, reinicia:

Terminal
sudo reboot

Luego reconecta por medio de SSH.

4. Instalar dependencias base.

Instalar herramientas esenciales:

Terminal
sudo apt install -y python3-venv python3-pip git nginx

Y verifica versiones:

Terminal
python3 --version
pip3 --version
git --version
nginx -v

5. Clonar el repositorio y preparar la app.

Ve a tu HOME y clona:

Terminal
cd ~
git clone https://github.com/TU_USUARIO/TU_REPO.git APP
cd APP

Si tu app necesita variables de entorno (por ejemplo secretos, strings de conexión, API keys), define una estrategia clara:

  • .env local (nunca lo subas a Git).
  • Variables en systemd (recomendado en este enfoque).
  • AWS SSM Parameter Store / Secrets Manager (más robusto, más AWS-native).

6. Crear y activar el entorno virtual.

Crea el venv dentro del proyecto:

Terminal
python3 -m venv venv
source venv/bin/activate

Confirma que estás usando el Python del venv:

Terminal
which python
python --version

7. Instalar dependencias Python.

Actualiza pip e instala requirements:

Terminal
pip install --upgrade pip
pip install -r requirements.txt

Si alguna dependencia exige una versión de Python que tu instancia no tiene, tendrás que:

  • Ajustar versiones en requirements.txt, o
  • Instalar una versión más nueva de Python (y recrear el venv)

Si tu app usa librerías con dependencias del sistema como psycopg2, lxml, pillow, tal vez necesites apt install adicional.

8. Prueba local con Gunicorn.

Antes de meter systemd y Nginx, valida que Gunicorn levanta tu app, para esto, ejecuta dentro del venv:

Terminal
python -m gunicorn --bind 127.0.0.1:8000 main:app

En otra sesión SSH o en la misma con otra terminal, prueba:

Terminal
curl http://127.0.0.1:8000

Si ves HTML o respuesta válida, tu app WSGI está bien expuesta.

Detén Gunicorn con Ctrl + C cuando termines la prueba.

9. Crear servicio systemd.

systemd permite que tu app:

  • Se levante al iniciar la instancia.
  • Reinicie si se cae.
  • Deje logs accesibles con journalctl para investigar incidencias.

Crea el servicio:

Terminal
sudo nano /etc/systemd/system/flaskapp.service

Ejemplo de configuración que ajusta rutas y nombre de módulo/app:

systemd
[Unit]
Description=Gunicorn service para APP (Flask/Dash)
After=network.target

[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/APP

# Variables de entorno (ejemplos):
# Environment="FLASK_ENV=production"
# Environment="APP_SECRET_KEY=..."
# Environment="DATABASE_URL=postgresql://..."
# Environment="PYTHONUNBUFFERED=1"

Environment="PATH=/home/ubuntu/APP/venv/bin"

ExecStart=/home/ubuntu/APP/venv/bin/gunicorn \
  --workers 2 \
  --timeout 120 \
  --bind unix:/home/ubuntu/APP/flaskapp.sock \
  main:app

Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

Aplica cambios:

Terminal
sudo systemctl daemon-reload
sudo systemctl start flaskapp.service
sudo systemctl enable flaskapp.service
sudo systemctl status flaskapp.service

Si falla, revisa logs:

Terminal
sudo journalctl -u flaskapp.service -n 50 --no-pager

10. Configurar Nginx.

Nginx recibirá tráfico en el puerto 80 y 443 si hay HTTPS y lo enviará al socket UNIX.

Crea un “server block”:

Terminal
sudo nano /etc/nginx/sites-available/flaskapp

Contenido recomendado:

Nginx
server {
    listen 80;
    server_name _;

    # Ajusta si tu app envía/recibe payloads grandes (Dash + dcc.Store)
    client_max_body_size 50M;

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/ubuntu/APP/flaskapp.sock:/;

        # Websocket/upgrade headers (útil en algunas apps interactivas)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeouts (útiles si tu app tarda en responder por cálculos pesados)
        proxy_connect_timeout 120s;
        proxy_send_timeout    120s;
        proxy_read_timeout    120s;
    }
}

Habilita el sitio y deshabilita el default:

Terminal
sudo ln -sf /etc/nginx/sites-available/flaskapp /etc/nginx/sites-enabled/flaskapp
sudo rm -f /etc/nginx/sites-enabled/default

Verifica y recarga:

Terminal
sudo nginx -t
sudo systemctl reload nginx

Prueba desde la propia instancia:

Terminal
curl http://127.0.0.1

Si responde, prueba desde tu navegador: http://IP_PUBLICA.

11. Abrir puertos y firewall UFW.

En Ubuntu, UFW puede estar deshabilitado por defecto. Si decides usarlo, una configuración típica:

Terminal
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status

12. Dominio, Elastic IP y HTTPS Certbot.

La IP pública por defecto puede cambiar al reiniciar/terminar la instancia. Una Elastic IP (EIP) te da una IP fija, para conseguirla dirìgete en AWS Console:

  • EC2 → Elastic IPs → Allocate Elastic IP.
  • Asócialo a tu instancia

Para una página personal o blog como este, lo normal es usar dominio + HTTPS con Certbot.

Instala Certbot:

Terminal
sudo apt install -y certbot python3-certbot-nginx

Ejecuta reemplazando con tu dominio:

Terminal
sudo certbot --nginx -d tu_dominio.com -d www.tu_dominio.com

Certbot editará Nginx y agregará bloques para 443, además de redirección a HTTPS si lo eliges.

Renovación automática para verificar:

Terminal
sudo systemctl status certbot.timer

Operación y mantenimiento

Logs y monitoreo.

Estado del servicio:

Terminal
sudo systemctl status flaskapp.service

Logs de Gunicorn vía systemd:

Terminal
sudo journalctl -u flaskapp.service -f

Logs de Nginx:

Terminal
sudo tail -n 100 /var/log/nginx/error.log
sudo tail -n 100 /var/log/nginx/access.log

Actualizar la aplicación.

Traer cambios desde Git:

Terminal
cd /home/ubuntu/APP
git pull

Si hay cambios en las dependencias, actualizar el requirements.txt:

Terminal
source venv/bin/activate
pip install -r requirements.txt
deactivate

Reiniciar servicio:

Terminal
sudo systemctl restart flaskapp.service
sudo systemctl status flaskapp.service

Recargar Nginx si cambiaste su config:

Terminal
sudo nginx -t && sudo systemctl reload nginx

Problemas comunes y soluciones: 500/502/504, timeouts, OOM

Esta sección resume un patrón típico al desplegar Flask + Dash en EC2. En local todo funciona muy bien, pero en producción aparecen errores en callbacks (/_dash-update-component), timeouts o respuestas 502/504.

1. 500 Internal Server Error en callbacks de Dash.

Revisa Logs de Gunicorn:

Terminal
sudo journalctl -u flaskapp.service -n 100 --no-pager

Las causas típicas son:

  • Excepciones dentro del callback.
  • Errores de importación en producción como paths diferentes.
  • Falta de variables de entorno como secrets, credenciales, etc.

Acciones recomendadas:

  • Asegura que las variables necesarias se declaran en systemd con Environment="...".
  • Imprime/monitorea errores en el backend (logging).

2. 502 Bad Gateway o 504 Gateway Timeout.

Las causas típicas son:

  • Gunicorn no está levantado o el socket no existe.
  • Nginx no tiene permisos para acceder al socket o al directorio.
  • El callback tarda demasiado y Nginx corta la conexión.

Acciones recomendadas:

  • Validar servicio:
  • Terminal
    sudo systemctl status flaskapp.service
  • Validar que el socket existe:
  • Terminal
    ls -l /home/ubuntu/APP/flaskapp.sock
  • Aumentar timeouts de Nginx si tu app tarda:
  • Terminal
    proxy_connect_timeout 120s;
    proxy_send_timeout    120s;
    proxy_read_timeout    120s;
  • Aumentar timeout de Gunicorn en ExecStart:
  • Terminal
    --timeout 120
  • Recargar servicios:
  • Terminal
    sudo systemctl daemon-reload
    sudo systemctl restart flaskapp.service
    sudo nginx -t && sudo systemctl reload nginx

3. OOM-kill por memoria insuficiente.

Las causas típicas son:

  • El sistema mata workers de Gunicorn.
  • Lo notarás en logs (journalctl) y con mensajes relacionados a memoria.

Acciones recomendadas:

  • Reduce workers a 1 o 2 en instancias pequeñas.
  • Considera agregar swap si estás en una instancia micro:
  • Terminal
    sudo fallocate -l 1G /swapfile
    sudo chmod 600 /swapfile
    sudo mkswap /swapfile
    sudo swapon /swapfile
    echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Swap ayuda a estabilidad, pero no es sustituto de RAM real. Si el uso de memoria es alto por diseño combinando pandas + grandes DataFrames, una instancia con más RAM suele ser la solución definitiva.

4. proxy_pass mal formado con socket UNIX.

Al usar sockets UNIX, el formato recomendado es:

Terminal
proxy_pass http://unix:/home/ubuntu/APP/flaskapp.sock:/;

El sufijo :/ suele evitar errores sutiles en el routing del proxy.

5. Permisos y 502 por acceso al socket.

Asegura permisos adecuados en directorios padre, por ejemplo:

Terminal
sudo chown -R ubuntu:www-data /home/ubuntu/APP
sudo chmod 750 /home/ubuntu/APP
sudo chmod o+x /home/ubuntu
sudo systemctl reload nginx

Checklist final

Antes de considerar “terminado” el deployment, valida:

  1. Security Group: SSH limitado, HTTP/HTTPS abiertos según necesidad.
  2. SSH con key .pem funciona y permisos del archivo están correctos.
  3. sudo apt update && sudo apt upgrade -y completado.
  4. nginx -v y python3 --version correctos.
  5. Repo clonado en /home/ubuntu/APP.
  6. venv creado y activable.
  7. pip install -r requirements.txt sin errores.
  8. python -m gunicorn --bind 127.0.0.1:8000 main:app responde con curl.
  9. Servicio systemd activo y habilitado sudo systemctl status flaskapp.service.
  10. Nginx OK:
    • sudo nginx -t.
    • curl http://127.0.0.1.
    • Navegador http://IP_PUBLICA.
  11. Ajustes de timeouts si hay callbacks pesados.
  12. client_max_body_size ajustado si hay payloads grandes.
  13. Ajustes de timeouts si hay callbacks pesados.
  14. Opcionalmente: EIP + dominio + HTTPS con Certbot.