streamlit run, puertos, modo headless y st.set_option

Básico
Streamlit
Streamlit
Actualizado: 26/04/2026

El comando streamlit run

El punto de entrada de cualquier aplicación Streamlit es el comando streamlit run:

streamlit run app.py

Esto inicia un servidor web en http://localhost:8501 y, por defecto, abre la aplicación en el navegador predeterminado.

flowchart LR
    A[streamlit run app.py] --> B[Servidor Tornado arranca]
    B --> C[localhost 8501 por defecto]
    C --> D{"Configuración?"}
    D -->|CLI flags| E[--server.port --server.headless]
    D -->|.streamlit/config.toml| F[Settings persistidos]
    D -->|st.set_option| G[Override en tiempo de ejecución]
    D -->|st.secrets| H[Credenciales seguras]
    H --> I[secrets.toml fuera de git]
    E --> J[Server escuchando]
    F --> J
    G --> J
    J --> K[Navegador conecta y ejecuta script]

Opciones de línea de comandos

El comando streamlit run acepta opciones para sobrescribir la configuración del archivo config.toml:

# Puerto personalizado
streamlit run app.py --server.port 8502

# Sin abrir el navegador (ideal para servidores)
streamlit run app.py --server.headless true

# Layout wide por defecto
streamlit run app.py --client.showSidebarNavigation true

# Nivel de logging
streamlit run app.py --logger.level debug

# Múltiples opciones combinadas
streamlit run app.py \
  --server.port 8080 \
  --server.headless true \
  --server.enableCORS false \
  --server.enableXsrfProtection false

La sintaxis es --<sección>.<opción> <valor>, que corresponde directamente a las secciones del config.toml.

Opciones de configuración más relevantes

Sección [server]

[server]
port = 8501                  # Puerto HTTP
headless = false             # false: abre el navegador; true: no lo abre
runOnSave = false            # Recarga automática al guardar el archivo
maxUploadSize = 200          # Tamaño máximo de archivos subidos (MB)
maxMessageSize = 200         # Tamaño máximo de mensajes WebSocket (MB)
enableCORS = false           # Necesario para embeber en iframes externos
enableXsrfProtection = true  # Protección CSRF

Sección [browser]

[browser]
gatherUsageStats = false     # Desactivar telemetría anónima
serverAddress = "localhost"  # Dirección que muestra en logs (cosmético)
serverPort = 8501

Sección [client]

[client]
showErrorDetails = true      # Mostrar traza de error completa en la UI
toolbarMode = "auto"         # "auto", "viewer", "minimal", "developer"
showSidebarNavigation = true # Mostrar navegación multipágina en sidebar

Sección [runner]

[runner]
magicEnabled = true          # Habilitar "st.write" implícito al poner variables solas
installTracer = false
fixMatplotlib = true         # Fix automático de backend Matplotlib

st.set_option: configuración en tiempo de ejecución

Algunas opciones pueden modificarse desde el propio código Python con st.set_option. Solo un subconjunto de opciones es modificable en tiempo de ejecución:

import streamlit as st

# Mostrar advertencias sobre uso obsoleto de APIs
st.set_option("deprecation.showfileUploaderEncoding", False)

# Habilitar/deshabilitar el modo "magic" (st.write implícito)
st.set_option("runner.magicEnabled", True)

# Configurar detalles del cliente
st.set_option("client.showErrorDetails", True)

Para la mayoría de configuraciones, es preferible usar el archivo config.toml ya que st.set_option en tiempo de ejecución tiene un alcance limitado.

Gestión de credenciales con st.secrets

st.secrets es la forma recomendada de gestionar credenciales (contraseñas, claves API, cadenas de conexión) en Streamlit. Las credenciales se definen en .streamlit/secrets.toml:

# .streamlit/secrets.toml  (NO subir a git)
db_password = "mi-contraseña-segura"
api_key = "sk-1234567890abcdef"

[database]
host = "localhost"
port = 5432
dbname = "mi_base_de_datos"
user = "admin"
password = "secreto"

[connections.mydb]
type = "sql"
url = "postgresql://admin:secreto@localhost:5432/mi_base_de_datos"

En el código Python, accede a los secretos como si fueran un diccionario:

import streamlit as st

# Acceso directo
api_key = st.secrets["api_key"]

# Acceso con punto (notación de atributo)
db_password = st.secrets.db_password

# Acceso a secciones anidadas
db_host = st.secrets["database"]["host"]
db_user = st.secrets.database.user

# Verificar si un secreto existe
if "api_key" in st.secrets:
    st.write("API key configurada.")

En Streamlit Community Cloud, los secretos se configuran en el panel de la app (Settings -> Secrets), no en el archivo secrets.toml del repositorio.

Variables de entorno como alternativa

Streamlit también puede leer variables de entorno, útil en entornos Docker o CI/CD:

import os
import streamlit as st

# Leer variable de entorno con valor por defecto
api_key = os.environ.get("OPENAI_API_KEY", "")

if not api_key:
    st.error("La variable de entorno OPENAI_API_KEY no está configurada.")
    st.stop()

Ejecutar Streamlit detrás de un proxy o en un servidor

Para desplegar Streamlit detrás de un proxy inverso (Nginx, Apache) o accesible desde una IP externa:

# config.toml para servidor
[server]
headless = true
port = 8501
enableCORS = false
# Ejecutar en todos los interfaces de red
streamlit run app.py --server.address 0.0.0.0 --server.headless true

Configuración de Nginx para proxy inverso:

server {
    listen 80;
    server_name mi-app.ejemplo.com;

    location / {
        proxy_pass http://localhost:8501;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

La clave es la directiva Upgrade para WebSockets, que Streamlit usa para la comunicación en tiempo real entre el servidor Python y el navegador.

Contexto: cómo arranca una aplicación Streamlit por dentro

Cuando ejecutas streamlit run app.py, ocurre una secuencia de eventos que conviene entender para depurar problemas de despliegue. Primero, Streamlit carga su servidor HTTP (basado en Tornado) en el puerto configurado. A continuación, expone un WebSocket por el que el frontend (un cliente React) se conecta para recibir actualizaciones en tiempo real. El script app.py no se ejecuta hasta que llega la primera conexión: cada usuario que abre la URL provoca una ejecución independiente del script en un contexto aislado.

Este modelo explica varias peculiaridades: por qué los cambios en el código se reflejan con runOnSave, por qué cada pestaña del navegador tiene su propio session_state, y por qué las operaciones pesadas (carga de modelos, consultas SQL) necesitan @st.cache_resource para compartirse entre sesiones. Entender el ciclo de vida del servidor es clave cuando diagnosticas problemas de rendimiento, latencia o memoria en producción.

Explicación línea por línea del ejemplo de configuración para servidor

streamlit run app.py \
  --server.port 8080 \
  --server.headless true \
  --server.enableCORS false \
  --server.enableXsrfProtection false
  1. --server.port 8080 sustituye el puerto predeterminado (8501) por 8080, útil cuando conviven varios servicios en la misma máquina.
  2. --server.headless true evita que Streamlit abra una pestaña del navegador al arrancar: imprescindible en servidores sin entorno gráfico.
  3. --server.enableCORS false desactiva las restricciones CORS, necesario cuando la app se embebe en un iframe de otro dominio.
  4. --server.enableXsrfProtection false desactiva la protección CSRF. Solo hazlo si ya tienes protección a nivel de proxy, porque desactiva una capa de seguridad importante.

Tabla de opciones más relevantes

| Sección | Opción | Por defecto | Uso típico | |---------|--------|-------------|------------| | [server] | port | 8501 | Cambiar puerto | | [server] | headless | auto | Servidores sin GUI | | [server] | runOnSave | false | Hot reload en desarrollo | | [server] | maxUploadSize | 200 | Subir ficheros grandes | | [server] | address | localhost | Exponer en red (0.0.0.0) | | [server] | baseUrlPath | "" | App bajo subruta (/myapp) | | [browser] | gatherUsageStats | true | Desactivar telemetría | | [client] | toolbarMode | auto | Ocultar menú en producción | | [client] | showErrorDetails | true | Esconder trazas en producción | | [theme] | base | light | Tema oscuro/claro | | [runner] | magicEnabled | true | Desactivar st.write implícito |

Errores comunes

El puerto 8501 ya está en uso. Ocurre cuando tienes otra instancia de Streamlit (o cualquier servicio) en ese puerto. Cambia el puerto con --server.port 8502 o mata el proceso anterior.

WebSocket bloqueado por el proxy. Si accedes a la app detrás de un proxy y ves que se queda "Connecting..." eternamente, el proxy no está reenviando los headers de Upgrade. Revisa la configuración Nginx/Apache y asegúrate de que proxy_set_header Upgrade $http_upgrade; está presente.

Secretos no encontrados. Si st.secrets["clave"] lanza KeyError, comprueba que el archivo está en .streamlit/secrets.toml relativo al directorio donde ejecutas streamlit run, no relativo al archivo app.py.

showErrorDetails=True en producción. Dejar esta opción activa en producción expone trazas de error con información sensible (rutas del servidor, nombres de variables, valores). Ponla a false en el config.toml de producción.

Cambios en config.toml que no se aplican. Streamlit lee config.toml solo al arrancar. Si editas el archivo, reinicia el servidor para que los cambios tengan efecto.

Mejores prácticas

  • Usa un .streamlit/config.toml distinto para desarrollo y producción. Mantén el de producción bajo control de versiones y el de desarrollo en .gitignore.
  • En servidores, siempre headless = true y runOnSave = false para evitar recargas no deseadas.
  • Configura maxUploadSize según el caso de uso real: 200 MB es el default, pero si tu app procesa videos, puede necesitar 500 MB o más.
  • Usa baseUrlPath cuando despliegues varias apps Streamlit en el mismo dominio bajo diferentes rutas.
  • Para Streamlit Community Cloud, deja la configuración por defecto y solo específica los secretos en el panel web.
  • Documenta en el README de tu proyecto todas las variables de entorno y claves de secrets.toml que la app necesita para funcionar.
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, 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

Ejecutar aplicaciones Streamlit con streamlit run y sus opciones de línea de comandos. Configurar puertos, modo headless y CORS para despliegues en servidor. Usar st.set_option para cambiar configuración en tiempo de ejecución. Gestionar credenciales de forma segura con st.secrets y secrets.toml. Comprender las opciones de configuración más relevantes del config.toml.