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.
# 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.
# 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"]
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 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
# 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.
- 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.