Run and share Streamlit apps

This example shows you how to run a Streamlit app with modal serve, and then deploy it as a serverless web app.

example streamlit app

This example is structured as two files:

  1. This module, which defines the Modal objects (name the script locally).
  2., which is any Streamlit script to be mounted into the Modal function (download script).
import pathlib

import modal

Define container dependencies

The script imports three third-party packages, so we include these in the example’s image definition. We also install asgiproxy to proxy the Streamlit server.

image = (
    .pip_install("streamlit", "numpy", "pandas")
    # Use fork until is merged.

stub = modal.Stub(name="example-modal-streamlit", image=image)

Mounting the script

We can just mount the script inside the container at a pre-defined path using a Modal Mount.

streamlit_script_local_path = pathlib.Path(__file__).parent / ""
streamlit_script_remote_path = pathlib.Path("/root/")

if not streamlit_script_local_path.exists():
    raise RuntimeError(
        " not found! Place the script with your streamlit app in the same directory."

streamlit_script_mount = modal.Mount.from_local_file(

Spawning the Streamlit server

Inside the container, we will run the Streamlit server in a background subprocess using subprocess.Popen. Here we define spawn_server() to do this and then poll until the server is ready to accept connections.

HOST = ""
PORT = "8000"

def spawn_server():
    import socket
    import subprocess

    process = subprocess.Popen(

    # Poll until webserver accepts connections before running inputs.
    while True:
            socket.create_connection((HOST, int(PORT)), timeout=1).close()
            print("Webserver ready!")
            return process
        except (socket.timeout, ConnectionRefusedError):
            # Check if launcher webserving process has exited.
            # If so, a connection can never be made.
            retcode = process.poll()
            if retcode is not None:
                raise RuntimeError(
                    f"launcher exited unexpectedly with code {retcode}"

Wrap it in an ASGI app

Finally, Modal can only serve apps that speak the ASGI or WSGI protocols. Since the Streamlit server is neither, we run a separate ASGI app that proxies requests to the Streamlit server using the asgiproxy package. Note that at this point asgiproxy has a bug with websocket handling, so we are using a fork with the fix for this.

    # Allows 100 concurrent requests per container.
def run():
    from asgiproxy.config import BaseURLProxyConfigMixin, ProxyConfig
    from asgiproxy.context import ProxyContext
    from asgiproxy.simple_proxy import make_simple_proxy_app


    config = type(
        (BaseURLProxyConfigMixin, ProxyConfig),
            "upstream_base_url": f"http://{HOST}:{PORT}",
            "rewrite_host_header": f"{HOST}:{PORT}",
    proxy_context = ProxyContext(config)
    return make_simple_proxy_app(proxy_context)

Iterate and Deploy

While you’re iterating on your screamlit app, you can run it “ephemerally” with modal serve. This will run a local process that watches your files and updates the app if anything changes.

modal serve

Once you’re happy with your changes, you can deploy your application with

modal deploy

If successful, this will print a URL for your app, that you can navigate to from your browser 🎉 .