Shipping the Demo: Docker and One-Click Deploys for Client Demos
Part 6 of the Python for FDE track. Last updated: October 2026.
"It works on my laptop" is where FDE demos go to die. The client needs to click the thing — on their machine, in their browser, without installing your toolchain. This post covers the two professional answers: Docker for "runs anywhere" packaging, and zero-Docker one-click hosts for when you need a public link in ten minutes. Either way, secrets travel in environment variables, never in the image.
The Dockerfile: your app, packaged
A Dockerfile is a recipe: start from a slim Python image, install dependencies, copy the code, expose the port, declare the start command. Generate it from Python so it lives in your project scaffolding:
from pathlib import Path
Path("Dockerfile").write_text("""\
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8501
CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]
""")
print("Dockerfile written")
# Dockerfile written
.dockerignore: keep the image lean and safe
Without a .dockerignore, your .env file (with real API keys) and gigabytes of CSVs get baked into the image. Exclude secrets, caches, and data — the image should contain code, not credentials:
from pathlib import Path
Path(".dockerignore").write_text("""\
__pycache__/
*.pyc
.env
.git/
*.csv
demo_ready/
""")
print(".dockerignore written")
# .dockerignore written
Build and run: the two commands
Two commands take you from source to a running container. The --env-file flag injects secrets at runtime — they live in the container's environment, not in the image layers:
# docker build -t client-demo:latest .
# docker run -p 8501:8501 --env-file .env client-demo:latest
# ... then open http://localhost:8501
# Sanity check from Python that the container is up:
import requests
r = requests.get("http://localhost:8501/_stcore/health", timeout=5)
print("Streamlit is up:", r.ok)
# Streamlit is up: True
Secrets: environment variables, everywhere
The rule is absolute: no secret is ever hardcoded, committed, or baked into an image. Read everything from the environment, with safe defaults only for non-secret config:
import os
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"] # KeyError fast if missing — good
CLIENT_API_KEY = os.environ["CLIENT_API_KEY"] # from --env-file .env, or the host
APP_ENV = os.getenv("APP_ENV", "demo") # non-secret config may have defaults
DEBUG = os.getenv("DEBUG", "false").lower() == "true"
print(f"Running in {APP_ENV} mode; debug={DEBUG}")
# Running in demo mode; debug=False
Health checks: prove the container is alive
Orchestrators and clients both want a health endpoint. If your demo also exposes an API (FastAPI from Post 1), add a trivial one — it turns "is it running?" from a question into a URL:
# api.py — add alongside your Streamlit app or as its own service
from fastapi import FastAPI
app = FastAPI()
@app.get("/healthz")
def healthz():
return {"ok": True, "service": "client-demo", "version": "1.0.0"}
Zero-Docker alternative: one-click hosts
Sometimes you need a public link now. Streamlit Community Cloud deploys straight from a GitHub repo; Hugging Face Spaces does the same with a small YAML front-matter in the README. Both read secrets from their dashboards, not your repo:
from pathlib import Path
# Hugging Face Spaces: this README front-matter picks the Streamlit SDK for you
Path("README.md").write_text("""\
---
title: Client Demo
emoji: 📊
sdk: streamlit
sdk_version: 1.32.0
app_file: app.py
---
# Client Demo
Deploys on push. Secrets: Space settings -> Variables and secrets.
""")
print("Space config written — push to Hugging Face and share the link")
# Space config written — push to Hugging Face and share the link
Key takeaways
- A Dockerfile (slim Python base, install, copy, expose, CMD) makes your demo run on any machine.
- .dockerignore keeps secrets, caches, and data out of the image.
- Inject secrets at runtime with --env-file; read them via os.environ with no hardcoded fallbacks.
- Add a /healthz endpoint so "is it running?" has a URL answer.
- Need a link in ten minutes? Streamlit Community Cloud or Hugging Face Spaces deploy from a repo push, with secrets in the dashboard.
Next in this series: Take-Home Build: End-to-End Client Integration Project.
Comments
Post a Comment