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
# 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"].git
__pycache__/
*.pyc
.venv/
.env
.pytest_cache/
Dockerfile
.dockerignoreBefore you build
- Use app.py as the entry script, or change the file name in the CMD line.
- List streamlit in requirements.txt together with your data libraries.
- Pin your dependencies in requirements.txt for reproducible builds.
How this Dockerfile works
FROM python:3.13-slim AS builderPackages are installed in a separate stage, so pip's leftovers and any compilers never reach the final image.
RUN pip install --no-cache-dir --prefix=/install -r requirements.txtInstalls Streamlit and your other libraries into one directory without keeping pip's download cache.
COPY --from=builder /install /usr/localPlaces the packages and the streamlit command where Python and the PATH already look.
RUN useradd --create-home --uid 10001 app USER appCreates an unprivileged user with a home directory, which Streamlit uses for its config and cache, and switches to it.
EXPOSE 8501Documents Streamlit's default port, so it's clear which port to publish or route a proxy to.
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
docker build -t streamlit-app .
docker run --rm -p 8501:8501 streamlit-appBuild 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.
| Variant | Runtime image | Image size | CI |
|---|---|---|---|
| Python slim (headless server) | python:3.13-slim | 560.8 MB | Passing |
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.