Streamlit Dockerfile for Production (Python Slim, Non-Root)

This Streamlit Dockerfile installs dependencies in a builder stage and runs your app on python:3.13-slim in headless mode as a non-root user. The server listens on port 8501 on all interfaces, ready for a reverse proxy or a container platform. It suits dashboards and internal data apps.

CI status
Build passing
Image size (uncompressed)
560.8 MB
Build time
29 s
Runtime image
python:3.13-slim
Port
8501
Last built
2026-10-15
Dockerfile
# syntax=docker/dockerfile:1
FROM python:3.13-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

FROM python:3.13-slim
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /app
COPY --from=builder /install /usr/local
COPY . .
RUN useradd --create-home --uid 10001 app
USER app
EXPOSE 8501
CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0", "--server.headless=true"]
.dockerignore
.git
__pycache__/
*.pyc
.venv/
.env
.pytest_cache/
Dockerfile
.dockerignore

Before you build

How this Dockerfile works

  1. FROM python:3.13-slim AS builder

    Packages are installed in a separate stage, so pip's leftovers and any compilers never reach the final image.

  2. RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

    Installs Streamlit and your other libraries into one directory without keeping pip's download cache.

  3. COPY --from=builder /install /usr/local

    Places the packages and the streamlit command where Python and the PATH already look.

  4. RUN useradd --create-home --uid 10001 app USER app

    Creates an unprivileged user with a home directory, which Streamlit uses for its config and cache, and switches to it.

  5. EXPOSE 8501

    Documents Streamlit's default port, so it's clear which port to publish or route a proxy to.

  6. CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0", "--server.headless=true"]

    Binds to all interfaces so the published port works, and headless mode skips the browser launch and the first-run email prompt.

Build and run

terminal
docker build -t streamlit-app .
docker run --rm -p 8501:8501 streamlit-app

Build from the project root and open http://localhost:8501. Pass API keys and database credentials as environment variables, not files in the image.

Other Streamlit Dockerfile variants

The slim image fits most Streamlit apps. Projects with heavy scientific libraries that need system packages may need extra apt installs, while uv-based variants speed up dependency installation. Verified variants appear in the table below.

VariantRuntime imageImage sizeCI
Python slim (headless server)python:3.13-slim560.8 MBPassing

Troubleshooting

The page loads but stays on 'Please wait...' behind a proxy
Streamlit uses WebSockets. Configure your reverse proxy to forward the Upgrade and Connection headers to the container.
PermissionError when the app writes a file
The app user can only write to its home directory and /tmp. Write output there, or mount a volume owned by the same UID.
ModuleNotFoundError for a library that works locally
The package isn't in requirements.txt. Freeze your environment, or add the missing package and rebuild the image.

FAQ

Why set server.address to 0.0.0.0?
Inside a container, an app bound to localhost can't be reached through the published port. Binding to all interfaces lets Docker forward traffic to Streamlit.
How do I add a health check?
Recent Streamlit versions answer on an internal health endpoint. Point your orchestrator's HTTP probe at port 8501, which avoids adding curl to the image.
Can I use secrets.toml in the container?
Yes, but mount it at runtime rather than copying it into the image, so credentials never end up in a registry.