Flask con Docker y contenedores

Avanzado
Flask
Flask
Actualizado: 18/04/2026

Dockerfile para Flask

Un Dockerfile define cómo construir la imagen de Docker para la aplicación Flask. Un Dockerfile bien diseñado es eficiente, seguro y reproducible.

Docker + Flask: contenerización y Docker Compose

Estructura del proyecto antes de Dockerizar:

mi_app_flask/
├── app/
│   ├── __init__.py
│   ├── models.py
│   └── routes.py
├── migrations/
├── .env.example
├── .dockerignore
├── Dockerfile
├── docker-compose.yml
├── gunicorn.conf.py
├── requirements.txt
└── app.py

Dockerfile básico para Flask con Gunicorn:

# Dockerfile
FROM python:3.13-slim

# Variables de entorno para Python
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

# Directorio de trabajo dentro del contenedor
WORKDIR /app

# Instalar dependencias del sistema necesarias
RUN apt-get update && apt-get install -y \
    gcc \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# Copiar e instalar dependencias Python primero (mejor cache de capas)
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip \
    && pip install --no-cache-dir -r requirements.txt

# Copiar el código de la aplicación
COPY . .

# Crear usuario no-root para seguridad
RUN useradd --create-home appuser
RUN chown -R appuser:appuser /app
USER appuser

# Exponer el puerto de Gunicorn
EXPOSE 5000

# Comando de arranque
CMD ["gunicorn", "-c", "gunicorn.conf.py", "app:app"]

El archivo .dockerignore evita incluir archivos innecesarios en la imagen:

# .dockerignore
.git
.gitignore
.env
*.pyc
__pycache__/
.pytest_cache/
.venv/
venv/
*.egg-info/
dist/
build/
.DS_Store

Dockerfile multi-stage para producción

Las imágenes multi-stage permiten usar una imagen grande para compilar y una imagen pequeña para ejecutar, reduciendo el tamaño final:

# Dockerfile.production
# ===== STAGE 1: Builder =====
FROM python:3.13-slim AS builder

WORKDIR /build

# Instalar dependencias de compilación
RUN apt-get update && apt-get install -y gcc libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# Instalar dependencias en un directorio virtual
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

# ===== STAGE 2: Producción =====
FROM python:3.13-slim AS production

# Solo instalar librerías de runtime (no compiladores)
RUN apt-get update && apt-get install -y libpq5 \
    && rm -rf /var/lib/apt/lists/*

# Copiar dependencias compiladas desde el stage builder
COPY --from=builder /install /usr/local

WORKDIR /app

# Copiar solo el código necesario
COPY app/ ./app/
COPY migrations/ ./migrations/
COPY app.py gunicorn.conf.py ./

# Usuario no-root
RUN useradd --no-create-home --shell /bin/false appuser
USER appuser

EXPOSE 5000

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
    CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000/health')"

CMD ["gunicorn", "-c", "gunicorn.conf.py", "app:app"]

Docker Compose para desarrollo

Docker Compose orquesta múltiples contenedores (Flask, base de datos, Redis, etc.) de forma declarativa:

# docker-compose.yml (DESARROLLO)
services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "5000:5000"
    environment:
      - FLASK_DEBUG=1
      - DATABASE_URL=postgresql://flaskuser:flaskpass@db:5432/flaskdb
      - REDIS_URL=redis://redis:6379/0
      - SECRET_KEY=dev-secret-key-insegura-solo-desarrollo
    volumes:
      # Montar el código local para recarga automática
      - .:/app
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    command: flask run --host=0.0.0.0 --debug

  db:
    image: postgres:17-alpine
    environment:
      POSTGRES_USER: flaskuser
      POSTGRES_PASSWORD: flaskpass
      POSTGRES_DB: flaskdb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U flaskuser -d flaskdb"]
      interval: 5s
      timeout: 5s
      retries: 5
    ports:
      - "5432:5432"  # Solo para acceso local con herramientas como pgAdmin

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
    ports:
      - "6379:6379"

volumes:
  postgres_data:
  redis_data:
# docker-compose.prod.yml (PRODUCCIÓN - override)
services:
  web:
    build:
      context: .
      dockerfile: Dockerfile.production
    environment:
      - FLASK_DEBUG=0
    env_file:
      - .env.production
    restart: unless-stopped
    # Sin mapeo de puerto al host; Nginx maneja las conexiones externas
    expose:
      - "5000"

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./static:/var/www/static:ro
      - /etc/letsencrypt:/etc/letsencrypt:ro
    depends_on:
      - web
    restart: unless-stopped

Comandos de Docker Compose:

# Desarrollo: construir e iniciar todos los servicios
docker compose up --build

# Producción: usar override de producción
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Ejecutar migraciones de base de datos
docker compose exec web flask db upgrade

# Ver logs en tiempo real
docker compose logs -f web

# Entrar al contenedor Flask
docker compose exec web bash

# Detener y eliminar contenedores
docker compose down

# Detener y eliminar contenedores + volúmenes (¡borra los datos!)
docker compose down -v

Variables de entorno y secretos en Docker

La gestión segura de secretos es crítica en entornos contenedorizados:

# Archivo .env.production (NUNCA subir a git)
SECRET_KEY=clave-super-segura-128-caracteres-random
DATABASE_URL=postgresql://prod_user:prod_pass@db-host:5432/prod_db
REDIS_URL=redis://redis-host:6379/0
MAIL_PASSWORD=api-key-sendgrid
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Para producción en plataformas cloud, usa variables de entorno del servidor en lugar de archivos .env:

# Render, Railway, Heroku: configurar en el panel web del servicio
# O mediante CLI de la plataforma

# Ejemplo con Railway CLI
railway variables set SECRET_KEY="clave-super-segura"
railway variables set DATABASE_URL="postgresql://..."

# Ejemplo con Heroku CLI
heroku config:set SECRET_KEY="clave-super-segura" --app mi-app-flask
heroku config:set DATABASE_URL="postgresql://..." --app mi-app-flask

Ruta de salud (healthcheck)

Añade un endpoint de salud para que Docker y los balanceadores de carga puedan verificar el estado:

# app/routes/health.py
from flask import Blueprint, jsonify
from app.extensions import db

health_bp = Blueprint('health', __name__)

@health_bp.route('/health')
def health_check():
    """Endpoint de salud para healthchecks de Docker y load balancers."""
    try:
        # Verificar conexión a base de datos
        db.session.execute(db.text('SELECT 1'))
        estado_db = 'ok'
    except Exception as e:
        estado_db = f'error: {str(e)}'

    estado = 'ok' if estado_db == 'ok' else 'degraded'

    return jsonify({
        'status': estado,
        'database': estado_db,
        'version': '1.0.0'
    }), 200 if estado == 'ok' else 503

Despliegue en Render

Render es una plataforma cloud que soporta despliegue directo desde GitHub con Docker:

# render.yaml
services:
  - type: web
    name: mi-app-flask
    env: docker
    dockerfilePath: ./Dockerfile.production
    plan: starter
    envVars:
      - key: FLASK_DEBUG
        value: "0"
      - key: SECRET_KEY
        generateValue: true
      - key: DATABASE_URL
        fromDatabase:
          name: mi-app-flask-db
          property: connectionString

databases:
  - name: mi-app-flask-db
    plan: free
    databaseName: flaskdb
    user: flaskuser

El uso de Docker en Flask garantiza que la aplicación se ejecuta de forma idéntica en todos los entornos, eliminando el clásico problema de "funciona en mi máquina".

Fuentes y referencias

Documentación oficial y recursos externos para profundizar en Flask

Documentación oficial de Flask
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Flask es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Flask

Explora más contenido relacionado con Flask y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Crear un Dockerfile optimizado para aplicaciones Flask con Gunicorn. Usar Docker Compose para orquestar Flask con base de datos y Redis. Gestionar variables de entorno y secretos en contenedores Docker. Construir imágenes multi-stage para reducir el tamaño en producción. Desplegar contenedores Flask en servicios cloud como Render o Railway.