Streamlit app packaged with PyInstaller always starts on port 3000 - how to override

Hi!

Background

  • The Streamlit app runner and app work as expected when the runner starts from the file system, with the app listening on port 8005
  • The app starts and throws no errors after bundling it with PyInstaller or cxFreeze, but starts on port 3000 no matter what the environment or command line arguments try to override

Objective

To configure Streamlit to start in port 8005 - need to understand how or where config.toml needs to be, or how to set the STREAMLIT_ environment variables so that they are visible to the runner or the app and start the application on port 8005 or other specified, and different from restricted port 3000.

Runner

#!/usr/bin/env python3
# See: https://github.com/pr4d4t0r/SSSCoring/blob/master/LICENSE.txt


from pathlib import Path
from pathlib import PurePath

import os
import sys

import streamlit.web.bootstrap


# *** functions ***

def _resolveRunnerPathFrom(bundlePath: str) -> str:
    path = PurePath(bundlePath)
    runnerPath = Path()
    for dir in path.parts:
        if dir == 'MacOS':
            break
        runnerPath /= dir
    # runnerPath = Path(runnerPath).joinpath('Resources', 'ssscrunner.py')
    runnerPath = Path(runnerPath).joinpath('MacOS', 'ssscrunner.py')
    return runnerPath.as_posix()


# *** main ***

if '__main__' == __name__:
    bundlePath = sys.argv[0]
    print('bundlePath = %s' % bundlePath)
    # Set environment variables to ensure production mode and specify the port
    os.environ["STREAMLIT_DEVELOPMENT_MODE"] = "false"  # This disables development mode
    os.environ["STREAMLIT_ENV"] = "production"
    os.environ["STREAMLIT_SERVER_ADDRESS"] = "localhost"
    os.environ["STREAMLIT_SERVER_HEADLESS"] = "true"
    os.environ["STREAMLIT_SERVER_PORT"] = "8005"

    sys.argv = [
        'streamlit',
        'run',
        'ssscrunner.py',
    ]
    streamlit.web.bootstrap.run(
        'ssscrunner.py',
        False,
        sys.argv[1:],
        {},
    )

All the packages and hooks are present. The PyInstaller .spec file is:

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

import site
import sys


site_packages = site.getsitepackages()[0]
block_cipher = None

a = Analysis(
    ['SSScore_app.py', 'ssscrunner.py'],
    pathex=[],
    binaries=[],
    datas=[
        (f"{site_packages}/plotly/validators", "plotly/validators"),
        (f"{site_packages}/plotly-*/", "."),
        (f"{site_packages}/plotly-*.dist-info/*", "plotly-6.2.0.dist-info/"),

        (f"{site_packages}/streamlit/static", "streamlit/static"),
        (f"{site_packages}/streamlit-*/", "."),
        (f"{site_packages}/streamlit-*.dist-info/*", "streamlit-1.46.1.dist-info/"),
    ],
    hiddenimports=[
        'streamlit',
    ],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    noarchive=False,
    optimize=0,
)

pyz = PYZ(a.pure)

exe = EXE(
    pyz,
    a.scripts,
    [],
    exclude_binaries=True,
    name='SSScore_app',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)
coll = COLLECT(
    exe,
    a.binaries,
    a.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name='SSScore_app',
)

Environment

  • macOS latest with Python 3.13.4, latest Streamlit, PyInstaller
  • Packaged in ./dist for now, until we can get it to work
  • The app doesn’t detect config.toml in pwd, or in ~/.streamlit
  • Browser window/tab starts in localhost:3000 and Problem with loading page - unable to connect
  • Startup environment is ignored

Output at startup

dist/SSScore_app/SSScore_app

Output:

bundlePath = dist/SSScore_app/SSScore_app
2025-07-09 22:05:41.369 WARNING streamlit.config:
Warning: the config option 'server.enableCORS=false' is not compatible with
'server.enableXsrfProtection=true'.
As a result, 'server.enableCORS' is being overridden to 'true'.

More information:
In order to protect against CSRF attacks, we send a cookie with each request.
To do so, we must specify allowable origins, which places a restriction on
cross-origin resource sharing.

If cross origin resource sharing is required, please disable server.enableXsrfProtection.

2025-07-09 22:05:41.374 DEBUG   streamlit.web.server.server: Starting server...
2025-07-09 22:05:41.375 DEBUG   streamlit.web.server.server: Serving static content from the Node dev server
2025-07-09 22:05:41.380 DEBUG   streamlit.web.server.server: Server started on port 8501
2025-07-09 22:05:41.380 DEBUG   streamlit.runtime.runtime: Runtime state: RuntimeState.INITIAL -> RuntimeState.NO_SESSIONS_CONNECTED

  You can now view your Streamlit app in your browser.

  Local URL: http://localhost:3000
  Network URL: http://10.2.0.2:3000

2025-07-09 22:05:41.788 DEBUG   streamlit.web.bootstrap: Setting up signal handler

I read as many standalone Streamlit posts as I could find here in the forums and elsewhere, none of the solutions offered allowed me to override port 3000.

Looking forward to your advise on how to fix this and thanks in advance!

Progress - Streamlit ignores the environment variable settings but can read:

  • $(pwd)/.streamlit/config.toml
  • $HOME/.streamlit.config.toml

That solved the initial issue of overriding the port with these settings:

[global]
developmentMode = false

[server]
port = 8509

[browser]
serverPost = 8509

Now figuring out how to handle creating the config.toml file. The simplest option appears to be:

  1. Runner starts
  2. Check for ~/.streamlit/config.toml
    1. Create the file if not present
    2. Update the file if present
  3. Continue initialization and start

The current targets are macOS and Linux. Still need to figure out how to package and configure for Windows.

Also exploring packaging .streamlit/config.toml in the bundle itself and setting the pwd to the bundle’s path. That way it won’t conflict with a pre-existing (and unrelated) $HOME/.streamlit/config.toml.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.