Using PyInstaller (or similar) to create an executable

Hello! I’ve been going through this forum looking for ways to share my streamlit app with colleagues. I have it on a virtual machine right now but I’m stuck on how to share the link from the virtual machine to non-technical coworkers. How did you go about doing it?

My Streamlit server runs on localhost of my VM (guest) on port 50000 (or whatever port you want), my VM is Linux so my localhost address is In my Virtualbox settings I have port forwarding enabled from guest to host. In my host (Windows) I had to enable firewall exceptions for port 50000, if I want other people to see my server, I simply share the network address of my host (for example 192.168.0.x) and the port (50000) and as long as the VM is running, people can see my streamlit app via their browser at the address: http://192.168.0.x:50000

Not sure if creating a batch file (as a one-click button) to open the environment
can do what you want…

(04:14 from the following video)

I Discovered a Way By which you can make an executable file without Pyinstaller
Just Click This Link And Read It I’ve Listed the Steps


Dear all,

thanks a lot for meaningful discussion.
I was also faced with the same problem.

Now, I found a solution without 404 not found error.


  • python = 3.7.9
  • streamlit = 0.71.0
  • pyinstaller = 4.1

After that, suppose we want to make an executable file from the following


import streamlit as st

if __name__ == '__main__':
    st.header("Hello world")


  1. Wrap the main script.

    • Make a wrapper script
    • Add the following lines to contained in the streamlit distribution, e.g. ${YOUR_CONDA_ENV}/lib/site-packages/streamlit/


import streamlit.cli

if __name__ == '__main__':
    streamlit.cli._main_run_clExplicit('', 'streamlit run')


def _main_run_clExplicit(file, command_line, args=[ ]):
    streamlit._is_running_with_streamlit = True, command_line, args)
  1. Create ./hooks/


from PyInstaller.utils.hooks import copy_metadata
datas = copy_metadata('streamlit')
  1. Create ./.streamlit/config.toml:


developmentMode = false

port = 8501
  1. (NEW) Edit run_main.spec which is created after pyinstaller --onefile --additional-hooks-dir=./hooks --clean:


# -*- mode: python ; coding: utf-8 -*-

block_cipher = None

a = Analysis([''],
pyz = PYZ(...)
exe = EXE(...)
  1. Finally, execute pyinstaller --onefile --additional-hooks-dir=./hooks run_main.spec --clean.


    - .streamlit/
        - config.toml
    - hooks/
    - run_main.spec
    - build/
        - run_main/
            - many .toc and .pyz
    - dist/
        - run_main.exe


The executable file created above does not work alone.
You should copy .streamlit and into dist direcoty.

Thank you :grinning:


I can confirm the method from @hmasdev.
This is awesome, thanks a lot!
It is even possible to edit the streamlit script ( in this case) during runtime as usual.

Nicely done!

Random (delusional?) thoughts…

Instead of making a streamlit binary for one app:

  • we could maybe create a generic streamlit runner that can execute any script passed in argument (or with a drag & drop onto the .exe)
  • and maybe associate an extension like / myapp.stpy so that it runs with that streamlit runner :smiley:
  • and maybe we could modify that specific version of streamlit to run in an electron app (cf. this issue)

There’s still the case of particular imports in your app, but we could imagine creating different streamlit runners which bundle commonly used libraries :smiley:


What kind of error message do you get? In my case it helped to modify recursion limit, see below. You could also try to define matplotlib as hidden import. This was necessary for some packages that weren´t imported correctly by PyInstaller.


>  #-*- mode: python ; coding: utf-8 -*-
> import sys
> sys.setrecursionlimit(sys.getrecursionlimit() * 5)
> block_cipher = None
> a = Analysis([''],
>              pathex=['.'],
>              binaries=[],
>              datas=[
>                  (
>                      "{$YOURPYTHONENV}/Lib/site-packages/altair/vegalite/v4/schema/vega-lite-schema.json",
>                      "./altair/vegalite/v4/schema/"
>                  ),
>                  (
>                      "${YOURPYTHONENV}/Lib/site-packages/streamlit/static",
>                      "./streamlit/static"
>                  )
>             ],
>             hiddenimports=['matplotlib'],
>             ...,
>             noarchive=False)
> pyz = PYZ(...)
> exe = EXE(...)
1 Like

I took a different approach to this. I used pyinstaller to create the single file exe, but the resulting Python used was actually a separate install, not the one made by pyinstaller. That’s so that my users can create new streamlit apps and use new sections of the dependencies that I wasn’t using. (Given pyinstaller would have stripped those out).

To see a demo of the exe see:

When first clicking it, it will unpack the distro. Subsequent clicks should be much quicker to boot. I need to make the terminal give the user some details and provide functionality for updates etc. But in its current form it is usable.

The script used to make that exe can be found over at:

And here is the pyinstaller magic. The following script is what pyinstaller is pointed to. It sees if the full streamlit python distro has been extracted, if it has it boots up streamlit in there, if it hasn’t it extracts it before doing so:

This whole approach should be easier now that I suspect the following PR has just released within 0.72.0:

All that will be required now is changing the following line:

To calling “python.exe -m streamlit run path/to/”

Very interesting, thank you for the step by step guide, I’ve managed to get it working!

However, can I check how long does your .exe take to load? Mine seems to take in excess of 1 minute, which seems pretty long. I’m already using a freshly created virtual environment, with only streamlit/pyinstaller installed.


I have created everything and followed your step by step guide. However when I have copied the files you mention in dist and then run the .exe file the app open in localhost:3000 so a different port than 8501.

My directory has all the relevant files and one folder called streamlit containing the config.toml file, and another one called hooks which has the file.

May I ask:

- .streamlit/
    - config.toml
- hooks/

the . in front of streamlit implies something different than the hooks? to my understanding streamlit and hooks are both folder in the directory of the project.

**when I run the everything is executed as expected. The app opens in localhost:8501

We need a step-by-step video tutorial.

Whoever does this will get the praise of living and dead.


Thanks @hmasdev, it works perfectly after a small adaptation to for Streamlit v0.82.0:


def _main_run_clExplicit(file, command_line, args=[], flag_options={}):
    streamlit._is_running_with_streamlit = True, command_line, args, flag_options)

Thank you for updating, @imad3v :slight_smile:

1 Like


The executable file created above does not work alone.
You should copy .streamlit and into dist directory.

Any way to make to the executable work standalone?

We can avoid using .streamlit/config.toml by using

streamlit run --global.developmentMode=false --server.port=8501

But how can we include within the executable to hide the code?

Hey Guys thanks for the tutorial and files, sorry I’m a newbie, so I have downloaded the 2 files , and i have changed the “python.exe -m pymedphys gui” by " python.exe -m streamlit run C:/Users/belba/PycharmProjects/MLTEST2/"

the test file is on GitHub - bello7777/Teston: streamlit exe template and work fine with streamlit sharing working

I got this error message related to pyinstaller , any help plsssss

C:\Users\belba\PycharmProjects\MLTEST2\venv\Scripts\python.exe C:/Users/belba/PycharmProjects/MLTEST2/
Traceback (most recent call last):
File “C:/Users/belba/PycharmProjects/MLTEST2/”, line 94, in
File “C:/Users/belba/PycharmProjects/MLTEST2/”, line 49, in main
_install(cwd, installation_path)
File “C:/Users/belba/PycharmProjects/MLTEST2/”, line 64, in _install
sys._MEIPASS # pylint: disable = no-member, protected-access
AttributeError: module ‘sys’ has no attribute ‘_MEIPASS’

Process finished with exit code 1


Hi @okld,

I suspect that @Belbaly_Nassim is trying to use the work I underwent. Above you mentioned the following:

A benefit of the approach I underwent is it doesn’t let pyinstaller strip out the dependencies, and it is compatible with any Python library (exe creators don’t need to deal with pyinstaller incompatibility).

I don’t currently have the bandwidth to help @Belbaly_Nassim, but might you, @okld, have time to jump on a quick ~30 min video call, where I can describe what I have done and potentially you could carry the baton onwards?



Any updates on this @okld @SimonBiggs, like @Belbaly_Nassim I also get the same error. Googling looks like it cant make a temporary folder?

A drag drop exe that can do this would be ideal. It’s just beyond my knowledge.


@seanbf, not as far as I know.

@SimonBiggs, still working on the matter? Is your work available somewhere on github so I can quickly check the current status?
You can DM me on discourse to organize a call if needed.

Hi @okld,

In fact, I just picked it back up today and gave it a shot. I did hit a dead end though, so if you were interested in further troubleshooting that’d be amazing.

I detailed the steps I was trying out over at:

1 Like