Deployment

Food Tracker (FastAPI)

Chapter 11 · Deployment

Every earlier chapter ran with uvicorn main:app --reload — a genuinely fine development server, and a genuinely wrong choice for real production traffic.

Running With Multiple Workers

pip install gunicorn gunicorn main:app -k uvicorn.workers.UvicornWorker -w 4 --bind 0.0.0.0:8000

Gunicorn manages several separate Uvicorn worker processes — a standard production pattern for FastAPI, giving real request-handling capacity beyond what a single process can provide, and automatically restarting a worker that crashes.

This course never needed CORS at all
Django, Express, and Firebase's own frontends each run as a genuinely separate process or origin from their own backend during development, requiring CORS configuration to let them talk to each other. This course's own Chapter 1 mounted its static frontend directly on the same FastAPI app via StaticFiles — the frontend and API have always shared one origin, in development and in production alike. There's no CORS middleware anywhere in this course, not because it was overlooked, but because Chapter 1's own architecture never created the cross-origin situation that would require it.

A Full-Circle Caveat on Chapter 1's Own Favorite Feature

Chapter 1 named automatic interactive docs at /docs as a genuine advantage none of this quartet's other courses get for free. Before a real deployment, that same feature deserves an honest second look:

# main.py import os app = FastAPI( title="Food Tracker", docs_url="/docs" if os.getenv("ENVIRONMENT") != "production" else None, redoc_url=None, )
The same feature, praised in Chapter 1, caveated here
A genuinely useful development tool and a genuinely public API explorer are not automatically the same thing to want live in production. /docs lets anyone browsing it try real API calls directly against the running app — fine, even valuable, during development; worth a deliberate decision, not an accidental default, before a real public launch. Disabling it conditionally, as above, keeps Chapter 1's own advantage intact during development while making its production exposure an explicit choice rather than something that just happens by default.

SQLite in Production: A Familiar Concern, and a New One

Food Tracker (React + Express)'s own deployment chapter named the risk of an ephemeral filesystem silently wiping the SQLite file on redeploy — the same risk applies here, for the same reason: confirm the deployment target keeps foodtracker.db on a volume that survives a restart.

This course introduces one genuinely new SQLite consideration the single-process Express deployment never had to face:

Multiple worker processes writing to one SQLite file
-w 4 above means four entirely separate operating-system processes, each with its own database connection to the same foodtracker.db file. SQLite handles multiple processes reasonably well for read-heavy workloads, but under genuine concurrent write contention across several processes at once, it can return real "database is locked" errors — a cost this course's own move to multiple worker processes trades away some of SQLite's single-process simplicity for. Chapter 10 already named that SQLite has no real connection pool the way a networked database does; this is the concrete, production-shaped consequence of that same limitation. At this app's own realistic single-household scale, simultaneous writes are genuinely rare, so a small worker count stays a reasonable choice — a busier real deployment would be a real, honest reason to move to PostgreSQL instead, exactly the same upgrade path Chapter 10 already named.

TLS Termination

Chapter 4's own camera access requires HTTPS in production. Gunicorn itself doesn't handle TLS certificates — a reverse proxy (nginx, the same pattern already covered in this site's own Nginx In Depth course) sits in front of it, terminating HTTPS and forwarding plain HTTP internally, the same general shape both the Django and Express siblings' own deployment chapters already used.

A Production Checklist

  • Run with gunicorn + Uvicorn workers, never --reload.
  • Decide deliberately whether /docs should be public, and disable it if not.
  • Confirm the SQLite file lives on a volume that survives a redeploy.
  • Keep worker count modest, or plan a PostgreSQL migration, if genuine concurrent writes are expected.
  • Put a reverse proxy in front for TLS termination.
  • Load real configuration (database path, environment name) from environment variables, not hardcoded values.

Where This Course Is Headed

One chapter left: a capstone tying every chapter into one complete, working app.

Hands-On Exercises

Exercise 1

Explain why this course never needed CORS configuration anywhere, tracing the reason back to a decision made in Chapter 1.

📄 View solution
Exercise 2

Explain why Chapter 1 named /docs as a genuine advantage, and why this chapter still recommends deliberately deciding whether to expose it in production rather than leaving the default as-is.

📄 View solution
Exercise 3

Explain why running with multiple gunicorn workers introduces a SQLite concern that Food Tracker (React + Express)'s own single-process deployment never had to face, and how this connects back to a point Chapter 10 already made.

📄 View solution

Chapter 11 Quick Reference

  • Run with: gunicorn -k uvicorn.workers.UvicornWorker -w 4, never --reload in production
  • No CORS anywhere: StaticFiles has served the frontend same-origin since Chapter 1 — a real simplicity payoff none of the three sibling courses share
  • Full-circle caveat: /docs, Chapter 1's own praised feature, should be a deliberate production decision, not an accidental default
  • Familiar SQLite risk: confirm a persistent volume, same as the Express sibling's own warning
  • New SQLite risk: multiple worker processes writing to one file can hit real lock contention — a direct consequence of Chapter 10's own "no real connection pool" point
  • TLS: a reverse proxy in front, same pattern as the Django and Express siblings
  • Next chapter: Capstone