FastAPI in Production: From main.py to a Public URL

A requirements.txt, a main.py and one click. Light Cloud starts uvicorn for you, and your interactive API docs are online at /docs.

FastAPI in Production: From main.py to a Public URL
On this pageShow
  1. What you will build
  2. Before you start
  3. Step 1: Create the project and a virtual environment
  4. Step 2: Write the API
  5. Step 3: Run it on your machine
  6. Step 4: Push it to GitHub
  7. Step 5: Deploy it
  8. Step 6: Use the live API and its docs
  9. Where data lives
  10. Troubleshooting

To deploy a FastAPI app on Light Cloud, put fastapi[standard] in requirements.txt, keep the app object as app in main.py, push to GitHub and click Deploy in the console. Light Cloud detects FastAPI, installs the requirements and starts uvicorn main:app on the container's port. No Dockerfile, no Procfile. The interactive docs at /docs are online with the API.

In my run the first deploy took 97 seconds.

What you will build

A small book API with validation by Pydantic, live on a light-cloud.io address, with its Swagger docs:

The Swagger UI docs page titled Reading List API with the routes GET /, GET /health, GET /books, POST /books and GET /books/{book_id}

The title, Reading List API, comes from an environment variable set in the console.

Source code: github.com/light-cloud-com/tutorial-fastapi-api.

Before you start

Step 1: Create the project and a virtual environment

terminal
$ mkdir tutorial-fastapi-api
$ cd tutorial-fastapi-api
$ python3 -m venv .venv
$ source .venv/bin/activate

On Windows, mkdir also prints a short table describing the new folder. Your prompt shows (.venv) once the virtual environment is active.

List the dependencies. fastapi[standard] brings uvicorn and the fastapi command with it; the version pin makes the build install exactly what you tested:

requirements.txt
text
fastapi[standard]==0.141.1
terminal
$ pip install -r requirements.txt
Successfully installed ... fastapi-0.141.1 ... pydantic-2.13.5 ... starlette-1.7.0 ... uvicorn-0.54.0 ...

Output is trimmed to the main packages.

Keep the virtual environment out of Git:

.gitignore
text
.venv/
__pycache__/
.env

Step 2: Write the API

main.py
python
import os

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field

# The title shows at the top of the interactive docs at /docs.
# Set API_TITLE on Light Cloud to change it without touching the code.
app = FastAPI(title=os.getenv("API_TITLE", "Book API"))


class BookIn(BaseModel):
    title: str = Field(min_length=1, max_length=200)
    author: str = Field(min_length=1, max_length=100)
    year: int = Field(ge=1450, le=2100)


class Book(BookIn):
    id: int


# In memory, so the list starts empty again after every restart.
books: dict[int, Book] = {}


@app.get("/")
def root():
    return {"message": "Hello from FastAPI", "docs": "/docs"}


@app.get("/health")
def health():
    return {"status": "ok"}


@app.get("/books")
def list_books() -> list[Book]:
    return list(books.values())


@app.post("/books", status_code=201)
def add_book(book: BookIn) -> Book:
    new = Book(id=len(books) + 1, **book.model_dump())
    books[new.id] = new
    return new


@app.get("/books/{book_id}")
def get_book(book_id: int) -> Book:
    if book_id not in books:
        raise HTTPException(status_code=404, detail="Book not found")
    return books[book_id]

Two details matter for the deploy. The file is called main.py and the FastAPI object is called app: that is what Light Cloud starts, as main:app. And the code never picks a port; the server that runs it does.

Step 3: Run it on your machine

terminal
$ fastapi dev main.py
 🌐 Server started at http://127.0.0.1:8000
    Documentation at http://127.0.0.1:8000/docs

Output is trimmed. In a second terminal, add a book and send one that breaks the rules:

terminal
$ curl -X POST http://127.0.0.1:8000/books -H "content-type: application/json" \
  -d '{"title":"The Pragmatic Programmer","author":"Andrew Hunt","year":1999}'
{"title":"The Pragmatic Programmer","author":"Andrew Hunt","year":1999,"id":1}
$ curl -X POST http://127.0.0.1:8000/books -H "content-type: application/json" \
  -d '{"title":"","author":"Nobody","year":3000}'
{"detail":[{"type":"string_too_short","loc":["body","title"],"msg":"String should have at least 1 character","input":"","ctx":{"min_length":1}},{"type":"less_than_equal","loc":["body","year"],"msg":"Input should be less than or equal to 2100","input":3000,"ctx":{"le":2100}}]}

The second request gets a 422 with both problems listed. Pydantic checked the body against BookIn before your function ran. Stop the server with Ctrl+C.

Step 4: Push it to GitHub

terminal
$ git init -b main
$ git add -A
$ git commit -m "FastAPI book API"
# Create an empty repository named tutorial-fastapi-api on github.com/new first.
$ git remote add origin https://github.com/YOUR-USERNAME/tutorial-fastapi-api.git
$ git push -u origin main
To https://github.com/YOUR-USERNAME/tutorial-fastapi-api.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.

Check that git status did not list .venv: the build installs the packages itself from requirements.txt.

Step 5: Deploy it

  1. In the Light Cloud console, click New..., then Deploy from GitHub.
  2. Search for tutorial-fastapi-api and click it.

Light Cloud shows FastAPI / Backend, based on requirements.txt, and Runs on a server:

The detection banner FastAPI Backend based on requirements.txt, with Runs on a server highlighted

  1. Open Advanced - build settings, environment variables, domain, scaling. Port is already 8000, the port uvicorn is started on. Leave it.

The Advanced section with Port 8000 highlighted

  1. Under Environment variables, click Add variable and add API_TITLE with the value Reading List API.

The Environment variables section with API_TITLE set to Reading List API

  1. Click Deploy and wait for Deployed (97 seconds in my run: the first build installs every package). The URL card has the address:

The Production overview of tutorial-fastapi-api with the Deployed badge and the URL card highlighted

Step 6: Use the live API and its docs

terminal
$ curl https://main-tutorial-fastapi-api-yourworkspace.light-cloud.io/
{"message":"Hello from FastAPI","docs":"/docs"}
$ curl -X POST https://main-tutorial-fastapi-api-yourworkspace.light-cloud.io/books -H "content-type: application/json" \
  -d '{"title":"The Pragmatic Programmer","author":"Andrew Hunt","year":1999}'
{"title":"The Pragmatic Programmer","author":"Andrew Hunt","year":1999,"id":1}

The first request after a quiet period takes a few seconds (3 seconds in my run) while an instance starts.

Now open /docs on your address. Swagger UI lists every route with the title from API_TITLE. Expand POST /books, click Try it out, edit the example and click Execute:

POST /books in Swagger UI with a Clean Code request body and the server response 201 with the created book

Your id may differ. In my run it was 3, although I had added only one book before, from a different terminal. That is the in-memory list at work: each instance keeps its own copy, and the next section explains why that matters.

Where data lives

The books dictionary lives in the memory of one running instance. On Light Cloud, as on any cloud platform:

  • a new deploy replaces the instances, so the list starts empty;
  • with Min instances at 0, the API stops when idle and starts empty on the next request;
  • under load several instances run at once, each with its own list.

That is fine for a demo and wrong for real data. Store it in a database: a frontend, two APIs and a database shows a FastAPI service with PostgreSQL through psycopg.

Troubleshooting

The deploy fails because the app cannot be found

Light Cloud starts uvicorn main:app, or app:app if the file is app.py. If your app is in src/api.py or the object has another name, uvicorn cannot find it. Move the entry point to main.py at the root with app = FastAPI(), or set Root directory to the folder that contains main.py.

FastAPI is not detected

Light Cloud looks for fastapi in requirements.txt or pyproject.toml at the root (or in the Root directory). A project that installs FastAPI only through another tool's lock file is not detected; add a requirements.txt.

More tutorials