How to share streamlit app locally without github for coworkers to launch?

Iā€™d like to easily share my Streamlit apps to co-workers. Requirements:

  • I want the app to be run locally because we are tackling confidential data
  • No need to go through Github
  • Co-workers are able to launch the app without Python code

How can I accomplish this?

1 Like

Hi @Tong,

Thanks for sharing your question! It looks like you shared this on StackOverflow as well, and got the following answers:

The question is what kind of user the target is. If itā€™s another coder, just create a requirements.txt file for your project and give them the source code (i.e. using an online repository like GitHub). Then they can recreate your environments and run the app via Streamlit locally themselves. If the target user is not a coder, you need to package everything, i.e. in a Docker container (see i.e. official documentation here docs.streamlit.io/knowledge-base/tutorials/deploy/docker)

Another option is to package your app as an executable ā€“ I recommend checking out this thread.

I donā€™t believe that answers the OP question; I would like to think that understand the OP question because I have the same exact desire.

I too found the post in Stackoverflow; but for ready access, here is a couple of follow-up questions.

Still within the corporate environment with machines in the same network and the ability to reach each otherā€¦is there a simple process for self-hosting?

For as long as upon launching, the streamlit program shows up in the browser in the one machine:

  • am I supposed to be able to reach the same program from another machine?
  • is there anything the keeps the program from accepting requests/connections?
  • is there something easy to configure so that this can be accomplished?

Desktop operating systems usually come with a preinstalled firewall, configured by default to block any incoming connections unless they come from localhost. You might have to configure the firewall to allow an application to listen to incoming requests in a certain port using a certain protocol.

In a corporate environment is common that IT people configure the operating system to comply with the organization policies before handing the computer to users, that are not granted the required permissions to change configurations.

If your operating system was configured by people in your org, those peple can answer your questions better than stranges in the Internet. If not, learn how to configure the firewall.

1 Like

I see; thanks for that.

And no, I donā€™t know firewall stuff; I am not a web developer, eitherā€¦thatā€™s why I am using Streamlit :wink: ā€¦to readily and simply convert a desktop Python program into a web-based one, to explore sharing, etc.

So, I do happen to have some additional privileges in a server box; and, late last night, I found another thread with some brief instructions on how to use docker to launch a streamlit appā€¦it worked; I can now reach my app from any computer.

2 Likes

hello gsal, could you share in depth what you did in order to accomplish? i believe that this gonna help to benefit users

Another handy way to do this is with ngrok, which allows you to expose a local port to the internet. https://ngrok.com/

e.g.

 # in one console window
streamlit run app.py
# In another
ngrok http 8501

Which generates a url that you can share with others to be able to access your app (as long as those commands keep running). Itā€™s not ideal for all scenarios, but Iā€™ve found it handy when just wanting to share something quickly without actually hosting it anywhere.

4 Likes

blackary:
I donā€™t know about ngrok. I think the OP and I are looking for a self-hosting option and not expose anything outside the company four walls. A brief look at ngrok webpage and it seems like it uses some server in Europe.

Tong
In my previous post, I included a link to the repo with the instructions I followedā€¦they are just a few lines, so, I can include them here:

  • Go to your working directory

  • Create a file named Dockerfile, with the following contents:

FROM python:3.9-slim

WORKDIR /app
COPY requirements.txt ./requirements.txt
RUN pip install -r requirements.txt
EXPOSE 8501
COPY myapp.py ./myapp.py
ENTRYPOINT ["streamlit", "run"]
CMD ["myapp.py"]
  • Copy your streamlit file over and rename it myapp.py, to match reference inside Dockerfile

  • Build: docker build -t myapp1:latest

  • Run: docker run -d -p 8501:8501 --name=testgui myapp1:latest

  • Go to browser and use app

  • Stop: docker stop testgui

  • Remove container: docker rm testgui

  • Remove image: docker rmi myapp1:latest


Oopsā€¦post-edit: You need the requirements.txt file, too.

So, like I said, the above steps kind of worked for me in that I was able to run the program in one machine and connect to it from another BUT, it only works onceā€¦for the first user to connect to itā€¦whatā€™s up with that?
Is this issue normal?
What else does not need to do for the app to work for multiple users?

1 Like

Does it mean that others are not able to access the web app when I exited anaconda prompt in my PC?

  • I am executing the docker command in a linux box
  • then, when at a terminal prompt, after I type docker run ..., I do not get the prompt back
  • which is the same thing that happens if you type: streamlit run <myapp>.py, when working locally, anyway; so,
  • I was not quite surprised about that

What surprises me is that even though supposedly there is some kind of web-server running, it is not able to handle more than one user; in other words, after I launch docker run... for the first time, the program only works for the one user that connects first; if a second user wants to use the program, it does not work correctly for them.

And so, in as much as I did not like the ngrok proposal, I speculate that the solution may be along those lines, i.e., some kind of additional web-server that can launch different instances of the app got subsequent users? Here is where I donā€™t know what I am talking about because I am not a web-developer.

FWIW, I just did what seemed to me the simplest thing that could possibly work and it actually worked.

  1. Launched a streamlit app in a device (endeavourOS without any special network configuration)
  2. Sent the link to the app url (letā€™s say http://192.168.0.10:8501) to a couple of other devices connected to the same local network (android)
  3. Open the link in each of the other devices.

This worked for me and I guess it should work in most domestic networks. As I said before, it may or may not work in your corporate network / with corporate devices, depending on corporate policies and how they are implemented.

1 Like

Hi, Goyoā€¦thanks for nothing :rofl: Your answer is as good as ā€œIt works for meā€ and it does not helpā€¦would you mind showing the precise steps you followed in that ā€œsimplest thing that could possibly workā€? I really doubt that it is a network issue, it seems more of a web server issue.

By the way, I was reading the Panel docs and, interestingly enough, they mention something interesting in relation to allowing different users having their own instance of the app while visiting the same web pageā€¦something about wrapping the final Python app back into a function. Panel uses the Bokeh server.

I enumerated the steps 1, 2 and 3. Let me know whatever you donā€™t understand. It is as simple as it looks. Launch the app in a computer (streamlit run app.py), look at the ā€œNetwork URLā€ that appears in the terminal, go to another computer, open a browser and point it to the aforementioned ā€œNetwork URLā€, thatā€™s all. I used two clients simultaneously because you said it only worked for one in your setup, so I wanted to test that.

I am not saying you are having network issues, that I donā€™t know. You were using docker and I barely know anything about it so I wonā€™t even speculate about what went wrong. It was just a warning that network issues can happen.

I donā€™t know about your issue about not handling multiple users, but the simplest case that @Goyo is describing is: launch your app locally the same way you always do: streamlit run my_app.py. There really arenā€™t any special instruction here.

Observe two things when you launch your app locally.

  1. The terminal will report two links to your running app. One via local host and one via your ip.
    image

  2. The window that automatically opens up is using the localhost version. The localhost url will not work on another computer. The ip version will (provided the other computer is on the same local network and any involved firewalls allow it, etc.).

My own network is pretty locked down so I hadnā€™t bothered to check if anything special needed to be done to offer up the app to the network. Thanks @Goyo for doing a test.

If you can open up your app on two different tab simultaneously locally, then the app is not having an issue with multiple users. Each tab is a separate session. If accessing the app from a different computer using the ip address link causes a limit on connections, I would suspect it is a network configuration issue.

Thank you both for clarifying things up, checking and double checking.

  • I tested and re-tested a couple of permutations,
  • from two different machines
  • with two different users
    and even
  • just one user, in one machine (locahost itself) and two browser tabs

And it was pretty consistentā€¦no matter user nor machine:

  • the first tab to upload the input file succeeds
  • the second tab to upload the input file fails

So, I went back to the source code and noticed a couple of things in play.

  1. the function validating the input file has the @cache decorator
  2. I am using a session state variable to track whether the input file passed validation

The skeleton code looks as follows:

import streamlit as st

st.session_state.inpfile_checked = False

@st.cache
def inpfile_validate():
    .
    .
    .
    if good:
        st.session_state.inpfile_checked = True
    else:
        st.session_state.inpfile_checked = False

inpfile = st.file_uploader("Input file", type='csv', key='inpfile')
inpfile_validate()
if st.session_state.inpfile_checked:
    pass
else:
    st.write('Error: Unacceptable file name.')

So, this is what I am seeing:

  • the first tab to upload the input file triggers the execution of inpfile_validate(), which succeeds
  • the first tab also triggers the caching of inpfile_validate()
  • by the time the second tab uploads the input file, the validate function is not executed because I am using the same exact input file; and, so, the session_state variable is left False

So, I believe this is telling me that:

  • the session_state variable is NOT being shared between tabs
  • the cached function IS being shared between tabs

After writing the last few lines above, I went ahead and tested with two different input file names; in this case, both tabs work.

Thoughts? ā€¦the caching thing?

A cache only caches the final output and no action within. Furthermore, it caches relative to its inputs, so youā€™d need to pass it the data of the file so it can cache the correct validation status for different files.

import streamlit as st

st.session_state.inpfile_checked = False

@st.cache
def inpfile_validate(uploaded_file):
    .
    # run check on locally defined uploaded_file variable
    .
    if good:
        return True
    else:
        return False

inpfile = st.file_uploader("Input file", type='csv', key='inpfile')
st.session_state.inpfile_checked = inpfile_validate(inpfile)
if st.session_state.inpfile_checked:
    pass
else:
    st.write('Error: Unacceptable file name.')

Note, I didnā€™t run a test with the st.cache to see if it correctly identifies files as ā€œthe sameā€ as youā€™d want. Since the file uploader returns an UploadedFile object which is a subclass of BytesIO, I am guessing it will perform as desired. Just a caveat that I didnā€™t validate that point with testing.

Well, I just found the one line:

  • Streamlit library
    • Advanced features
      • Optimize performane with st.cache:

ā€œThis happens because the Streamlit cache is global to all users. So everyone contributes to everyone elseā€™s performance.ā€

Between the above statement and what you and the docs mention regarding caching of inputs and outputs, one also needs to make sure that the function does not have any side effects. In my case, the side effect was changing the value of a global variable without it being an input nor an outputā€¦this side effect does not happen after the function has been cached.

Correct. Your function (originally) was cached with nothing in, nothing out, so it became a dud as soon as it was first executed.

Hello @mathcatsand

Thanks alot for your solution. I have a question if you may . I want to change the network url to something more neat like www.myapp.com . Can you share with me some resources to do so?

It works. Thank you.