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.

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
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.