FastAPI: From Basics to Production · Deployment, Observability and Performance
Docker Images for FastAPI
Build small, cache-friendly, non-root images.
Use an official slim Python base, install dependencies before copying source (layer caching), consider uv for fast, locked installs, run as a non-root user, and use the exec form of CMD so signals reach the server. Keep secrets out of the image (inject at runtime) and add a health endpoint for orchestrators.
FROM python:3.13-slim
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-install-project
COPY . .
RUN useradd --create-home appuser && chown -R appuser /app
USER appuser
EXPOSE 8000
CMD ["/app/.venv/bin/fastapi", "run", "app/main.py", "--port", "8000"]
Common pitfalls
- Copying the whole repo before installing dependencies (cache busts on every change).
- Shell-form CMD swallowing SIGTERM.
- Baking
.envfiles into images.
Key points
- Dependencies before source for caching
- Non-root, exec-form CMD
- Secrets injected at runtime
Further reading: FastAPI: Docker
Practice questions
Why use exec-form CMD ["..."] instead of shell form?
Answer
Exec form runs the server as PID 1 directly so it receives SIGTERM and can shut down gracefully; shell form wraps it in /bin/sh -c, which may not forward signals.
What does PYTHONUNBUFFERED=1 do in containers?
Answer
It disables output buffering so logs appear immediately in docker logs / the log collector.
Open in the app to mark lessons complete, take notes and track your progress.