FastAPI: From Basics to Production · Deployment, Observability and Performance

Docker Images for FastAPI

Build small, cache-friendly, non-root images.

Create a free account to save this page, take notes and track your progress. Sign in

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 .env files 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.

Was this page helpful?