Dockerfile para Streamlit
Un Dockerfile bien construido para Streamlit debe:
- Usar una imagen base ligera de Python
- Copiar solo los archivos necesarios
- Instalar las dependencias desde
requirements.txt - Exponer el puerto 8501 (puerto por defecto de Streamlit)
- Configurar la app para ejecutarse en modo headless
# Dockerfile
FROM python:3.12-slim
# Crear usuario no root para mayor seguridad
RUN useradd -m -u 1000 appuser
# Directorio de trabajo
WORKDIR /app
# Copiar dependencias primero (aprovecha la caché de capas de Docker)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copiar el código de la aplicación
COPY . .
# Cambiar al usuario no root
USER appuser
# Exponer el puerto de Streamlit
EXPOSE 8501
# Variables de entorno para producción
ENV STREAMLIT_SERVER_HEADLESS=true \
STREAMLIT_SERVER_PORT=8501 \
STREAMLIT_BROWSER_GATHER_USAGE_STATS=false
# Comando de inicio
CMD ["streamlit", "run", "app.py", "--server.address=0.0.0.0"]
flowchart TD
A[Código app.py y requirements.txt] --> B[Dockerfile python 3.12 slim]
B --> C["COPY requirements + RUN pip install"]
B --> D[COPY código proyecto]
B --> E[EXPOSE 8501]
B --> F[ENTRYPOINT streamlit run]
F --> G[docker build -t mi-app]
G --> H[docker run -p 8501 mi-app]
G --> I[docker compose con postgres redis]
I --> J[Servicios orquestados]
H --> K[App corriendo aislada]
J --> K
K --> L[Deploy a servidor o cloud]
L --> M[Render Heroku ECS Cloud Run]
.dockerignore
Evita incluir archivos innecesarios en la imagen Docker:
# .dockerignore
.git
.gitignore
.venv
__pycache__
*.pyc
*.pyo
.pytest_cache
*.egg-info
.streamlit/secrets.toml # ¡NUNCA incluir secretos en la imagen!
node_modules
*.md
tests/
Construir y ejecutar la imagen
# Construir la imagen con una etiqueta de versión
docker build -t mi-app-streamlit:1.0 .
# Ejecutar el contenedor
docker run -p 8501:8501 mi-app-streamlit:1.0
# Ejecutar en segundo plano con reinicio automático
docker run -d \
--name mi-app \
--restart unless-stopped \
-p 8501:8501 \
mi-app-streamlit:1.0
# Pasar secretos como variables de entorno (en lugar de secrets.toml)
docker run -d \
--name mi-app \
-p 8501:8501 \
-e DATABASE_URL="postgresql://user:pass@host/db" \
-e OPENAI_API_KEY="sk-..." \
mi-app-streamlit:1.0
Accede a la aplicación en: http://localhost:8501
Acceder a secretos de entorno en Streamlit
import streamlit as st
import os
# Leer desde variables de entorno (alternativa a secrets.toml en Docker)
database_url = os.environ.get("DATABASE_URL") or st.secrets.get("database_url", "")
api_key = os.environ.get("OPENAI_API_KEY") or st.secrets.get("openai_api_key", "")
if not database_url:
st.error("Variable DATABASE_URL no configurada.")
st.stop()
Docker Compose: Streamlit + PostgreSQL
Para aplicaciones que requieren una base de datos, docker-compose.yml orquesta ambos servicios:
# docker-compose.yml
version: "3.9"
services:
app:
build: .
ports:
- "8501:8501"
environment:
- DATABASE_URL=postgresql://appuser:secreto@db:5432/miapp
depends_on:
db:
condition: service_healthy
volumes:
- ./assets:/app/assets:ro # Montar assets como solo lectura
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: miapp
POSTGRES_USER: appuser
POSTGRES_PASSWORD: secreto
volumes:
- postgres_data:/var/lib/postgresql/data
- ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d miapp"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
# Levantar todos los servicios
docker compose up -d
# Ver logs en tiempo real
docker compose logs -f app
# Parar y eliminar los contenedores
docker compose down
# Reconstruir la imagen tras cambios
docker compose up -d --build
Dockerfile multietapa para producción
Para reducir el tamaño de la imagen final:
# Etapa 1: instalar dependencias
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt
# Etapa 2: imagen final mínima
FROM python:3.12-slim
WORKDIR /app
# Copiar solo las dependencias instaladas, no el pip entero
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
ENV STREAMLIT_SERVER_HEADLESS=true \
STREAMLIT_SERVER_PORT=8501
EXPOSE 8501
CMD ["streamlit", "run", "app.py", "--server.address=0.0.0.0"]
Componentes personalizados de la comunidad
Streamlit permite instalar componentes creados por la comunidad para añadir funcionalidades no incluidas en el core. Se instalan con pip:
streamlit-aggrid: tabla avanzada con filtros y edición
pip install streamlit-aggrid
import streamlit as st
import pandas as pd
from st_aggrid import AgGrid, GridOptionsBuilder
df = pd.DataFrame({
"nombre": ["Ana García", "Carlos López", "María Martínez"],
"departamento": ["Ingeniería", "Marketing", "Ventas"],
"salario": [55000, 42000, 38000],
"activo": [True, True, False]
})
gb = GridOptionsBuilder.from_dataframe(df)
gb.configure_pagination(paginationAutoPageSize=True)
gb.configure_default_column(editable=True, filter=True, sortable=True)
gb.configure_column("salario", type=["numericColumn", "numberColumnFilter"])
grid_options = gb.build()
resultado = AgGrid(
df,
gridOptions=grid_options,
height=300,
fit_columns_on_grid_load=True
)
st.write("Datos modificados:", resultado["data"])
streamlit-folium: mapas interactivos con Folium
pip install streamlit-folium folium
import streamlit as st
import folium
from streamlit_folium import st_folium
# Crear mapa centrado en Madrid
mapa = folium.Map(location=[40.4168, -3.7038], zoom_start=12)
# Añadir marcador
folium.Marker(
[40.4168, -3.7038],
popup="<b>Madrid</b><br>Capital de España",
tooltip="Haz clic para más info"
).add_to(mapa)
# Mostrar en Streamlit
resultado = st_folium(mapa, width=700, height=450)
if resultado["last_object_clicked"]:
st.write("Clic en:", resultado["last_object_clicked"])
streamlit-lottie: animaciones Lottie
pip install streamlit-lottie requests
import streamlit as st
import requests
from streamlit_lottie import st_lottie
def cargar_lottie(url: str):
r = requests.get(url)
return r.json() if r.status_code == 200 else None
animacion = cargar_lottie("https://assets5.lottiefiles.com/packages/lf20_jcikwtux.json")
col1, col2 = st.columns([1, 2])
with col1:
if animacion:
st_lottie(animacion, height=200, key="carga")
with col2:
st.title("Cargando datos...")
st.caption("Animación Lottie integrada en Streamlit")
streamlit-option-menu: menú de navegación personalizado
pip install streamlit-option-menu
import streamlit as st
from streamlit_option_menu import option_menu
with st.sidebar:
seleccion = option_menu(
menu_title="Menú principal",
options=["Inicio", "Dashboard", "Análisis", "Configuración"],
icons=["house", "bar-chart", "search", "gear"],
menu_icon="cast",
default_index=0
)
if seleccion == "Inicio":
st.title("🏠 Inicio")
elif seleccion == "Dashboard":
st.title("📊 Dashboard")
elif seleccion == "Análisis":
st.title("🔍 Análisis")
elif seleccion == "Configuración":
st.title("⚙️ Configuración")
Arquitectura de un componente personalizado
Los componentes personalizados tienen dos partes:
- Frontend: código HTML/CSS/JavaScript (React opcional) que renderiza el componente en el navegador
- Backend Python: clase o función que hace el puente entre el frontend y el script de Streamlit
# Ejemplo mínimo de cómo está construido un componente internamente
import streamlit.components.v1 as components
# Declarar el componente (en el paquete del componente)
mi_componente = components.declare_component(
"mi_componente",
url="http://localhost:3001" # URL del servidor de desarrollo del frontend
)
# Uso en la aplicación
valor = mi_componente(texto="Hola", key="comp1")
st.write(f"Valor devuelto por el componente: {valor}")
Para usar un componente HTML estático sin framework:
import streamlit.components.v1 as components
# Incrustar HTML arbitrario
components.html(
"""
<div style="background: linear-gradient(135deg, #FF4B4B, #FF8C00);
padding: 20px; border-radius: 10px; color: white; text-align: center;">
<h2>Componente HTML personalizado</h2>
<p>Puede contener cualquier HTML, CSS y JavaScript.</p>
</div>
""",
height=150
)
# Incrustar un iframe
components.iframe("https://docs.streamlit.io", height=400, scrolling=True)
Despliegue en la nube con la imagen Docker
Una vez construida la imagen, puedes desplegarla en cualquier proveedor cloud:
# Google Cloud Run
gcloud run deploy mi-app-streamlit \
--image gcr.io/mi-proyecto/mi-app-streamlit:1.0 \
--platform managed \
--region europe-west1 \
--allow-unauthenticated \
--port 8501
# AWS App Runner (desde ECR)
aws ecr get-login-password | docker login --username AWS --password-stdin 123456789.dkr.ecr.eu-west-1.amazonaws.com
docker tag mi-app-streamlit:1.0 123456789.dkr.ecr.eu-west-1.amazonaws.com/mi-app:latest
docker push 123456789.dkr.ecr.eu-west-1.amazonaws.com/mi-app:latest
# Azure Container Apps
az containerapp create \
--name mi-app-streamlit \
--image miregistro.azurecr.io/mi-app:1.0 \
--target-port 8501 \
--ingress external
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, Streamlit 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 Streamlit
Explora más contenido relacionado con Streamlit y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Escribir un Dockerfile optimizado para una aplicación Streamlit de producción. Crear una imagen Docker y ejecutar la aplicación con docker run. Usar Docker Compose para orquestar Streamlit junto a PostgreSQL u otros servicios. Instalar y usar componentes personalizados populares de la comunidad Streamlit. Entender la arquitectura de un componente personalizado (frontend. Python backend).