Dockerfile for running streamlit

Sharing a working Dockerfile incorporating many best practices for deploying Streamlit.

Successful built application using

  • Streamlit 1.40
  • Python 3.13
  • Debian/Ubuntu linux container - bookworm-slim (not Alpine)
  • FastAPI 0.115.6
  • openai 1.57.1
  • pytest, pytest-mock, unittest, unittest-mock etc

If you want to pull from GitHub β†’ Github link
There are some additional comments in my GitHub.

# initialize new build stage
FROM python:3.13.1-slim-bookworm AS compiler

# stops Python from writing pyc files and buffering stdin/stdout
ENV PYTHONWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

# set working directory in container. / is  root of file system in container.
WORKDIR /market_news

# To remove Jupyter Notebook files, uncheck block below
# no significant size reduction but may remove CVE risks
#RUN apt-get update && apt-get remove --purge -y jupyter-notebook \
#    && apt-get autoremove -y \
#    && apt-get clean \
#    && rm -rf /var/lib/apt/lists/* /tmp/*

# install necessary build tools then clean up
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    curl \
    software-properties-common \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* \
# install virtualenv and create a virtual environment
# why virtualenv instead of venv: https://virtualenv.pypa.io/en/latest/
    && pip3 install virtualenv \
    && virtualenv /.venv
    
# activate and use virtual environment
ENV PATH="/.venv/bin:$PATH"

# Copy requirements.txt into container's working dir
COPY requirements.txt .

# install packages, using pip in virtual (not global) environment
RUN pip3 install --no-cache-dir -r requirements.txt


# final build stage
FROM python:3.13.1-slim-bookworm AS final

# arguments for creating a non-root user
ARG USERNAME=non-root-username-goes-here
ARG USER_UID=1001
ARG USER_GID=${USER_UID}

# create non-root user with no shell access and no home directory
# for alpine, it is adduser and addgroup
RUN groupadd --gid=${USER_GID} --system ${USERNAME} \
    && useradd --no-create-home --shell=/bin/false --no-log-init \
    --uid=${USER_UID} --gid=${USER_GID} --system ${USERNAME}

# set the working directory
WORKDIR /market_news

# activate virtual environment in final stage
ENV PATH="/.venv/bin:$PATH"

# copy virtual environment from builder stage into virtual environment in final stage
COPY --from=compiler /.venv /.venv

# copy application code
COPY . .

# chown - change ownership of /market_news directory to new non-root user
# set directory's permission to read and execute only (555), ensure the non-root user cannot modify the application's code
RUN chown -R ${USER_UID}:${USER_GID} /market_news \
    && chmod -R 555 /market_news

# Set user to non-root user
USER ${USERNAME}

# expose Streamlit's default port
EXPOSE 8501

# test if container is working. 1 is for unhealthy status, so exit if unhealthy.
HEALTHCHECK CMD ["curl", "--fail", "http://localhost:8501/_stcore/health", "||", "exit", "1"]

ENTRYPOINT ["/.venv/bin/streamlit", "run", "project.py", "--server.port=8501"]

# To execute:
# docker build -t [image-name] .
# Either:
# docker run -d -p 8501:8501 --name=[container-name] [image-name]
# -d runs container in detached mode (in background)
# -p 8501:8501 is to map port 8501 on host to port 8501 in container
# Or:
# streamlit run app.py
2 Likes