Getting Started with FastAPI Using uv

FastAPI is one of the fastest-growing Python web frameworks, thanks to its speed, modern syntax, and ease of use. But when you combine FastAPI with uv for dependency management and Docker Compose watch for live reloading, you get a development workflow that’s both fast and frictionless.

In this guide, we’ll set up a FastAPI project from scratch using uv for dependency management, run it in Docker, and use compose watch to sync changes instantly.


1. Why uv?

uv is a drop-in replacement for pip and virtualenv that manages Python packages and environments with blazing speed. It’s perfect for modern Python development, and it integrates cleanly into container builds, reducing image build times and making dependency management more reliable.


2. Getting Started

First, install uv with Homebrew; other instructions are on their installation page.

brew install uv

Now, let's set up the project directory and use python 3.12

uv init -p 3.12 python-fastapi-uv-example

This creates for us a python-uv-example directory and the following files:

.
├── main.py
├── pyproject.toml
└── README.md

1 directory, 3 files

Delete the main.py. It's not needed for this app.


3. The FastAPI App

Now, let's create a simple FastAPI app that retrieves the server's IP address.

Create a new file: app/main.py:

import requests
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

def get_ip_address():
    try:
        response = requests.get('https://api.ipify.org?format=json')
        response.raise_for_status()
        ip_data = response.json()
        return ip_data.get('ip', 'IP address not found')
    except requests.RequestException as e:
        return f"Error occurred: {e}"

@app.get("/ip")
async def get_client_ip(request: Request):
    client_host = request.client.host
    return JSONResponse(content={"ip": get_ip_address()})

This app calls the ipify API to retrieve the public IP address of the server running the container.


4. Adding Dependencies with uv

For this, we'll need to add some dependencies:

uv add fastapi --extra standard
uv add requests
[project]
name = "python-fastapi-uv-example"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "fastapi[standard]>=0.116.1",
]

To test out the app run:

uv run fastapi dev

Then curl the endpoint:

curl localhost:8000/ip

5. Dockerfile with uv

Adding uv from the astral repo.

# syntax=docker/dockerfile:1.4
FROM python:3.12-slim AS builder

# Install uv.
COPY --from=ghcr.io/astral-sh/uv:0.8 /uv /uvx /bin/

WORKDIR /app
COPY pyproject.toml uv.lock .
RUN uv sync --frozen

COPY app ./app

FROM python:3.12-slim
WORKDIR /app

COPY --from=builder /app /app

EXPOSE 8000

# Run the application.
CMD ["/app/.venv/bin/fastapi", "run", "app/main.py", "--port", "8000", "--host", "0.0.0.0"]

Build the container

docker build -t python-fastapi-uv-example:latest .

And run it

docker run -p 8000:8000 python-fastapi-uv-example:latest

Again, test it with curl localhost:8000/ip


6. Docker Compose with Watch

Create docker-compose.yaml:

services:
  develop:
    build: .
    ports:
      - "8000:8000"
    develop:
      watch:
        - action: sync
          path: .
          target: /app
          ignore:
            - .venv/
        - action: rebuild
          path: ./pyproject.toml

The develop.watch feature syncs local changes into the container instantly and rebuilds if pyproject.toml changes.


7. Running the Project

Build the container and run watch.

docker compose build && docker compose up --watch

With Watch enabled, any changes to your code will instantly update in the running container. No manual restarts required.

Note: make sure you do a build each time you do a pull when the container is not running so that you have the latest code before running the watch.

8. Testing the API

Visit:

http://localhost:8000/ip

You’ll get back JSON with the server’s public IP address:

{"ip": "203.0.113.42"}

Next Steps

Now go ahead and add linting and formatting to make your development life-cycle easier with Python Linting.


Final Thoughts

By combining FastAPI with uv and Docker Compose Watch, you get a rapid feedback loop without the usual friction of rebuilding containers for every code change. This setup is ready to be expanded into a full production workflow, while still keeping local development lightning fast.

I've added the repo for this article on github: https://github.com/markcallen/python-fastapi-uv-example