Concepts 🚀 Project 🧮 Labs
📚 Learn / Build OpenBenchML — Step by Step / Docker + Render — Ship It to the Internet
🚀 Step 10 of 10
← Previous: Step 9 — Notebook Kernel — Run User Python in the Browser

Docker + Render — Ship It to the Internet

Package the app into a Docker image, push to GitHub, connect Render, and go live. From localhost to a public URL in one step.

🎯 Goal — what you'll achieve in this step Run `git push`, wait 5 minutes, visit https://yourapp.onrender.com and see your live app with auth, upload, benchmark, leaderboard, and notebook.
📖 The basic concept Learn this first. It's the pure Python/HTML idea, stripped of all app complexity.
# The basic concept: Docker = reproducible environment

# Without Docker:
#   "Works on my machine" — different Python, different pip, different OS
#   Deploy = SSH, install deps, pray

# With Docker:
#   Build an image once: docker build -t myapp .
#   Run it anywhere: docker run -p 8000:8000 myapp
#   Same image runs on laptop, Render, AWS, GCP, Oracle Cloud

# A Dockerfile is a recipe:
#   FROM python:3.11-slim    (start from a base image)
#   COPY requirements.txt .  (copy deps file)
#   RUN pip install ...      (install deps)
#   COPY . .                 (copy code)
#   CMD ["uvicorn", ...]     (what to run)

# Each line is a layer. Change code? Only the COPY layer rebuilds.
# Change requirements.txt? pip install layer rebuilds. Fast iteration.
🧮 Apply it to OpenBenchML
📄 Dockerfile dockerfile
# Step 10: Package the app
FROM python:3.11-slim

WORKDIR /app

# Install system deps (if any)
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential && rm -rf /var/lib/apt/lists/*

# Install Python deps (cached layer)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy the app code
COPY . .

# Create the uploads directory
RUN mkdir -p uploads

# Expose the port
EXPOSE 8000

# Run with uvicorn
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
📄 requirements.txt text
fastapi==0.104.1
uvicorn[standard]==0.24.0
sqlalchemy==2.0.23
python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4
python-multipart==0.0.6
jinja2==3.1.2
scikit-learn==1.3.2
numpy==1.26.2
📄 render.yaml yaml
# Render deployment config
# Render reads this file and creates the service automatically
services:
  - type: web
    name: openbenchml
    env: docker
    healthCheckPath: /health
    envVars:
      - key: SECRET_KEY
        generateValue: true    # Render generates a random secret
      - key: DATABASE_URL
        value: sqlite:///data.db
    disk:
      name: data
      mountPath: /opt/render
      sizeGB: 1
📄 deploy.sh bash
# Step 10: Deploy to Render

# 1. Push your code to GitHub
git init
git add .
git commit -m 'OpenBenchML step 10: ready to deploy'
git remote add origin https://github.com/YOUR_USERNAME/openbenchml.git
git push -u origin main

# 2. Go to render.com -> New -> Web Service
# 3. Connect your GitHub repo
# 4. Render detects render.yaml and creates the service
# 5. Wait ~5 minutes for the build
# 6. Visit https://openbenchml.onrender.com

# To update: just `git push` — Render auto-deploys on every push to main.

# Test locally with Docker first:
docker build -t openbenchml .
docker run -p 8000:8000 openbenchml
# Visit http://localhost:8000

How it works

Docker solves 'works on my machine' — the image contains the exact Python, pip packages, and OS libraries, so it runs identically everywhere. The Dockerfile recipe: (1) FROM python:3.11-slim starts from a minimal Python base. (2) COPY requirements.txt + RUN pip install installs deps FIRST so this layer is cached — rebuilding after a code change skips the slow pip install. (3) COPY . . copies your code. (4) EXPOSE 8000 documents the port. (5) CMD [...] is the command that runs when the container starts. Render is a PaaS: you push to GitHub, Render detects the Dockerfile + render.yaml, builds the image, runs it on a managed VM, gives you a URL, terminates HTTPS, and restarts on crash. The healthCheckPath: /health tells Render how to check if the app is alive — if /health returns non-200, Render restarts the container. The SECRET_KEY env var is auto-generated by Render (generateValue: true) — never commit secrets to git. From this point on, `git push` IS the deploy command.

✏️ Try yourself Add a docker-compose.yml that runs the app + a Postgres database together. Add a .dockerignore file (exclude .git, __pycache__, *.db) to make builds faster. Set up a custom domain in Render's dashboard.
⚠️ Common mistakes to avoid
  • Binding to `host='127.0.0.1'` in the Dockerfile CMD — the app is unreachable from outside the container. Always use `--host 0.0.0.0`.
  • Hardcoding SECRET_KEY in the Dockerfile — anyone with the image can forge JWTs. Use env vars: `SECRET_KEY = os.getenv('SECRET_KEY')`.
  • Not adding a healthCheckPath — Render can't tell if the app is alive, so it may restart healthy containers or keep dead ones running.
  • Forgetting to persist the SQLite file — Render's filesystem is ephemeral. Use a disk mount (render.yaml) or switch to Postgres for production data.
  • Committing .git, __pycache__, or the .db file into the Docker image — bloats the image and leaks secrets. Add a .dockerignore.
🔍 Where this is used in OpenBenchML Dockerfile + render.yaml at the project root. The same image runs on Render (free tier) and on Oracle Cloud (24 GB RAM Always Free tier for the notebook kernel).