Jupyterhub + Streamlit

I just managed to get streamlit working inside of JupyterHub.

Prereqs: Make sure to have https://github.com/jupyterhub/jupyter-server-proxy installed + enabled.

streamlit hello --browser.serverAddress --server.enableCORS False

optional: --server.port 8501 (in case you want to run it on a different port, like one opened in the container)

Running Jupyterhub with Docker, each user gets their own container with one of several pre-built environments they can choose from a drop-down list.

Issues I ran into:

  1. Finding documentation about command-line arguments was difficult. I didn’t have access to edit ~/.streamlit/config.toml because of restricted access, hence the command-line.
  2. Installing serverproxy in a container that didn’t already have it pre-installed doesn’t seem to work. No explanation for why. Using an image that came with serverproxy was fine. Intuition is that it shouldn’t matter, experiments have shown otherwise.
  3. CORS must be disabled. Stuck “loading” if not. I take care of all SSL at the nginx-level, and proxy-pass to the jupyterhub instance, so the safety wasn’t a concern for me (hub is running on http). Let me know if I’m wrong (I’m not a web-dev).
  4. Had to visit user/michael/proxy/5000/ (and the trailing slash does matter)
  5. Technically, the user-image that worked for me used the following install instructions, not pip install:
 git clone --depth 1 https://github.com/jupyterhub/jupyter-server-proxy && \
    cd jupyter-server-proxy/jupyterlab-server-proxy && \
    npm install && npm run build && jupyter labextension link . && \
    npm run build && jupyter lab build

I ran that in a container that didn’t have the proxy installed, didn’t fix lack of access.

At PyDataLA right now, so I don’t quite have the time to dig in and build a minimally-working Dockerfile, but that’s definitely a next step for debugging.

A bloated image (with R/Julia/Octave/Python) that does allow the proxy-pass can be found on docker hub at mathematicalmichael/math-user:latest

Got a minimal working example! Reproduction steps:


FROM jupyter/minimal-notebook:latest
RUN pip install streamlit jupyter-server-proxy jupyter-rsession-proxy

So, apparently all that was missing was the jupyter-rsession-proxy, which I happened to be using to get RStudio working in JupyterHub.
Anyway, make that Dockerfile and then run:

docker built -t streamlit-image .
docker run --rm -p 8888:8888 streamlit-image jupyter lab --NotebookApp.token=''
  1. Visit port 8888 on localhost (or the IP of your remote machine, as in my case).
  2. Open Terminal and run streamlit hello --browser.serverAddress --server.enableCORS False
  3. In your address bar, swap /lab with /proxy/8501/ (trailing slash important!)
  4. Enjoy the wonderful goodness that is the streamlit demo

This should make app-demos fairly easy. Swap jupyter lab ... for the streamlit command, map 8888:8501 instead and use nginx to proxy-pass to

In practice… That hasn’t worked for me. I suspect it’s some problem in my nginx config (headers?). I’m able to hit the app but see “Please wait”. Web console says something about an unspported state transition.
Would love help with that…
With my above plan, if I visit the IP of my remote machine at port 8888 instead of the web address that I was attempting to proxy-pass, the app runs (on http)! That’s promising. Basically says that we have a docker-run command now:

docker run --rm -p 8501:8501 streamlit-image streamlit hello --browser.serverAddress (CORS seems to only be an issue for the proxy-via-jupyterlab approach).


This is so amazing. Now I have somewhere to point people to when this comes up. Thanks for coming to the talk today!!

docker build* not built

also not really having a whole lot of success with this in an already-running JLab. It seems like if it exists upon startup, this solution works. But now I’m on someone else’s jupyter deployment so I don’t have the ability to do anything except pip install from JLab’s terminal. Hitting 404 errors.

quick update on using with docker-machine (I didn’t install the desktop edition on my new mac, figuring out the kinks/differences as I go, finally got around to testing streamlit).

since docker won’t bind localhost to the docker-machine IP, you’ll have to disable CORS for local development.

@Adrien_Treuille @mathematicalmichael I just came across this older thread, and wanted to link to one I posted recently, below.

I’ve been working on running Streamlit in JupyterHub but without using Docker. I also have experience doing this with Docker too, so if I get a chance I’ll go through your notes and see if I can write something up based on our ideas combined.

Thanks for this example @mathematicalmichael! I took this and wrapped it a bit further by adding streamlit to the jupyter config so that you just need to go to /streamlit to access the server.

I’ve got an example on renkulab.io that anyone can fork and extend if they wish to play around with it: https://renkulab.io/projects/rok.roskar/streamlit-demo

The key piece is the jupyter_notebook_config.py file that defines streamlit as a server extension so if you want to run your own piece of code you would need to amend that file (or add a second spec).

1 Like

I’m actually having some trouble getting around some of the new security. does yours still work with latest streamlit?

updated Dockerfile that I hope can help people figure out the associated instructions

# base image
FROM python:3.7

# streamlit-specific commands
RUN mkdir -p /root/.streamlit
RUN bash -c 'echo -e "\
email = \"\"\n\
" > /root/.streamlit/credentials.toml'
RUN bash -c 'echo -e "\
enableCORS = false\n\
enableXsrfProtection = false\n\
" > /root/.streamlit/config.toml'

# exposing default port for streamlit

# copy over and install packages
COPY requirements.txt ./requirements.txt
RUN pip3 install -r requirements.txt

# copying everything over
#COPY . .
RUN useradd appuser && chown -R appuser /st
USER appuser

# run app
CMD streamlit run
>cat ~/bin/streamlit 
docker run --rm --name streamlit -d -p 8501:8501 -v $(pwd):/tmp -w /tmp $IMAGE_NAME $COMMAND $@ --browser.serverAddress --server.enableCORS False --server.enableXsrfProtection False

so from within jupyter terminal this would be: streamlit run app.py --browser.serverAddress --server.enableCORS False --server.enableXsrfProtection False

1 Like