The go-to resource for upgrading Python, Django, Flask, and your dependencies.

FastAPI 0.115 to 0.120: New Lifespan Events Replace on_startup and on_shutdown


In FastAPI 0.115 and later, the @app.on_event(“startup”) and @app.on_event(“shutdown”) decorators are deprecated. The new lifespan parameter, powered by an async context manager, provides a more reliable way to handle application startup and shutdown. We’ll walk through the reasons for this change, compare the old and new approaches, and outline the migration process.

The Original on_event Decorators

Before FastAPI 0.115, we used @app.on_event decorators to register async functions for startup and shutdown events.

from fastapi import FastAPI

app = FastAPI()
models = {}

@app.on_event("startup")
async def startup():
    models["ml"] = load_ml_model()  # DB/ML init

@app.on_event("shutdown")
async def shutdown():
    models.clear()  # Cleanup

@app.get("/predict")
async def predict():
    return {"result": models["ml"].predict()}

As you can see, we load the model on startup and clean up on shutdown. Though, there are limitations: no guaranteed cleanup (no yield mechanism), it only runs on the main app (sub-apps ignored), and memory leaks can occur with uvicorn —reload.

The New Lifespan Approach (0.115+)

With the new approach, we pass a lifespan parameter—an async context manager—to the FastAPI constructor. Code before the yield executes on startup; code after, on shutdown.

from contextlib import asynccontextmanager
from fastapi import FastAPI

models = {}

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup: Load shared resources
    models["ml"] = load_ml_model()
    yield  # App runs here
    # Shutdown: Cleanup
    models["ml"].close()
    models.clear()

app = FastAPI(lifespan=lifespan)
AspectOld on_eventNew lifespan
Async MgrNoYes (yield)
Sub-appsRuns main onlyMain only
Uvicorn ReloadLeaksSafe
Deprec?Yes 0.115+No
DB/ML FitManualContext-safe

Migration Steps

  1. Audit your codebase for on_event usage: grep -r “@app.on_event” .
  2. Replace the decorators with a lifespan context manager:
    # OLD
    @app.on_event("startup")
    async def load_db():
        engine = create_engine(app.config["DB_URL"])
    
    # NEW → lifespan
    @asynccontextmanager
    async def lifespan(app: FastAPI):
        engine = create_engine(app.config["DB_URL"])
        yield
        engine.dispose()
    app = FastAPI(lifespan=lifespan)
  3. Test: Run pytest and start the server with uvicorn --reload to verify reload behavior.
  4. Upgrade FastAPI: pip install "fastapi>=0.115"
  5. Verify: Check logs for proper startup and shutdown order.

Real-World: DB Pool + Alembic

from sqlalchemy import create_engine
from alembic import command

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup DB
    engine = create_engine(app.config["DATABASE_URL"])
    with engine.connect() as conn:
        conn.execute(text("CREATE SCHEMA IF NOT EXISTS app"))
    command.upgrade(engine, "head")  # Migrations
    app.state.engine = engine
    yield
    # Shutdown
    app.state.engine.dispose()

app = FastAPI(lifespan=lifespan)

@app.get("/health")
async def health():
    with app.state.engine.connect() as conn:
        return {"status": conn.scalar(text("SELECT 1"))}

Pytest example:

import pytest
from httpx import AsyncClient
from main import app

@pytest.fixture
async def client():
    async with AsyncClient(app=app, base_url="http://test") as ac:
        yield ac

@pytest.mark.asyncio
async def test_health(client):
    resp = await client.get("/health")
    assert resp.status_code == 200

Uvicorn/Gunicorn Notes

  • --reload: Lifespan restarts cleanly (no leaks).
  • Gunicorn gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker
  • Sub-apps (app.mount): Lifespan main-only, mount startup separate.

Why Migrate Now?

  • Future-proof: The on_event decorators will be removed in a future version.
  • Reliable cleanup: The yield ensures shutdown code runs even on exceptions.
  • Performance: Shared state like connection pools is managed efficiently.
  • Docs: FastAPI Lifespan

Questions? Comment below or FastAPI Discord.

<RelatedLinks {relatedLinks} />

Sponsored by Durable Programming

Need help maintaining or upgrading your Python application? Durable Programming specializes in keeping Python apps secure, performant, and up-to-date.

Hire Durable Programming