The three big data-app frameworks share one hosting profile
Streamlit, Gradio, and Dash dominate Python data apps in 2026. Despite different APIs, they share a hosting shape:
- A long-running Python web server (often WebSocket-backed).
- In-memory session state that doesn't love horizontal scaling.
- A near-universal need for a database or warehouse to back the visuals.
- Bursty, human-driven traffic — busy during work hours, idle overnight.
Get that profile right and all three deploy the same way. This post compares hosting approaches and shows the deploy specifics for each framework.
Framework quick orientation
| Framework | Strength | Server model | Typical use |
|---|---|---|---|
| Streamlit | Fastest script-to-app | WebSocket, reruns top-to-bottom | Dashboards, internal tools |
| Gradio | ML demos & inputs | WebSocket/HTTP, event-driven | Model demos, ML UIs |
| Dash (Plotly) | Production analytics | Flask + callbacks (HTTP) | Polished BI apps |
Dash is built on Flask, so it's the most "normal" web app of the three and the easiest to scale horizontally. Streamlit and Gradio lean on WebSockets and in-memory state.
What to look for in a host
- Persistent process + WebSocket support.
- A managed database nearby (Postgres/MySQL) with the connection string handy.
- Secrets as env vars.
- Custom domain + SSL.
- Cost that follows usage — scale-to-zero or a cheap floor for idle dashboards.
- Live logs for debugging the inevitable "works locally" issues.
The options
Framework-native clouds
Streamlit Community Cloud and Hugging Face Spaces are purpose-built and frictionless for public apps. Limits: privacy, resources, and the lack of an integrated production database push serious apps elsewhere. Dash has no single official free cloud (Plotly offers a commercial enterprise product).
Raw VPS
Run the server under systemd, add Caddy for TLS, install Postgres. Cheap and total control; you own everything operationally. The database and backups are on you.
Managed container PaaS
One pattern for all three frameworks: containerize, expose the port, attach a managed DB. The platform handles build/deploy/TLS/domains. Best fit when you want production-grade data apps without running a server.
PandaStack
PandaStack runs all three as container apps (auto-detected Python or your Dockerfile, built with rootless BuildKit), provisions a managed PostgreSQL/MySQL with DATABASE_URL injected, gives custom domains with automatic SSL, server-side metrics and analytics (ClickHouse, no client SDK), and free-tier scale-to-zero for idle internal tools. Caveat: free-tier DBs are dev/hobby-sized and free apps cold-start on preemptible nodes — fine for internal dashboards, upgrade for customer-facing.
Deploy snippets per framework
All three need to bind 0.0.0.0 and the platform's port.
Streamlit
streamlit run app.py --server.port=$PORT --server.address=0.0.0.0 --server.headless=trueGradio
import os
demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))Dash (with gunicorn for production)
# app.py
import dash
app = dash.Dash(__name__)
server = app.server # expose the Flask servergunicorn app:server --bind 0.0.0.0:$PORT --workers 4Note Dash can use multiple gunicorn workers because callbacks are stateless HTTP — a real scaling advantage over Streamlit/Gradio.
Connecting to the database
Cache the connection/pool so you don't reconnect on every interaction:
import os, psycopg_pool
pool = psycopg_pool.ConnectionPool(os.environ["DATABASE_URL"], max_size=10)
def query(sql, params=()):
with pool.connection() as conn, conn.cursor() as cur:
cur.execute(sql, params)
return cur.fetchall()In Streamlit wrap this in @st.cache_resource; in Dash a module-level pool is fine; in Gradio create it once at import.
The scaling gotcha
- Streamlit & Gradio: keep to a single replica unless you externalize session state. Multiple replicas + in-memory state = inconsistent UX.
- Dash: scales horizontally cleanly with gunicorn workers and replicas because state lives client-side or in the DB.
- All three: mind the DB connection count. Free-tier databases cap connections (e.g., 50 on PandaStack free) — use a small pool, not one connection per session.
Cost strategy
Internal dashboards are idle ~16 hours a day. Scale-to-zero turns that idle time into near-zero cost at the price of a cold start on first hit — perfect for internal tools. For a dashboard customers depend on, pay for a warm instance and skip the cold start. Don't pay always-on prices for an app three people open at 9 a.m.
My recommendation
For a quick public share, framework-native clouds win. For a real internal or production data app backed by a database, use a container platform that bundles a managed DB so you're not stitching together a separate database provider. PandaStack does this in one place — container app + managed Postgres + SSL + scale-to-zero — and its server-side analytics means you can see dashboard usage without bolting on a tracking SDK.
References
- Streamlit deployment: https://docs.streamlit.io/deploy
- Gradio sharing/deployment: https://www.gradio.app/guides/sharing-your-app
- Dash deployment guide: https://dash.plotly.com/deployment
- Gunicorn docs: https://docs.gunicorn.org/en/stable/
- psycopg connection pools: https://www.psycopg.org/psycopg3/docs/advanced/pool.html
---
Hosting a Streamlit, Gradio, or Dash app that needs a real database? PandaStack's free tier gives you the app and a managed Postgres in one place. Start at https://dashboard.pandastack.io