Forums

FastAPI + a2wsgi deployment on PythonAnywhere: WSGI loads successfully, but still getting errors

Hi everyone,

I'm trying to deploy a small FastAPI application on PythonAnywhere using a2wsgi. I've made a lot of progress, but I'm still stuck and wanted to document everything I've tried in case I'm overlooking something obvious.

Project structure

trimly/
├── backend/
│   ├── app/
│   ├── .env
│   ├── trimly.db
│   └── ...
├── frontend/
├── venv/

The virtual environment is located at:

/home/trimly/trimly/venv

PythonAnywhere configuration

Source code

/home/trimly/trimly/backend

Working directory

/home/trimly/

Virtualenv

/home/trimly/trimly/venv

Python version:

3.13

WSGI file

import sys

project_home = "/home/trimly/trimly/backend"

if project_home not in sys.path:
    sys.path.insert(0, project_home)

from a2wsgi import ASGIMiddleware
from app.api import app

application = ASGIMiddleware(app)

Things I verified 1. Virtualenv is correct

source /home/trimly/trimly/venv/bin/activate
which python

returns

/home/trimly/trimly/venv/bin/python
  1. a2wsgi is installed

    python -c "import a2wsgi; print(a2wsgi.file)"

works successfully.

  1. FastAPI imports correctly

    cd ~/trimly/backend

    python -c "from app.api import app; print(app)"

returns a FastAPI instance.

  1. uWSGI starts successfully

The server log shows:

WSGI app 0 (mountpoint='') ready in 2 seconds

There are no import errors after that.

Initial problem

Initially I received:

ModuleNotFoundError: No module named 'a2wsgi'

That turned out to be an older error. After fixing the virtual environment and confirming a2wsgi was installed, the application started loading correctly.

Application

The FastAPI app uses a lifespan function:

@asynccontextmanager
async def lifespan(app: FastAPI):
    Base.metadata.create_all(bind=engine)

    with engine.begin() as conn:
        conn.execute(
            text(
                "INSERT OR IGNORE INTO sqlite_sequence(name, seq) VALUES ('urls', 9999)"
            )
        )

    yield

app = FastAPI(lifespan=lifespan)
Database configuration
engine_kwargs: dict[str, Any] = {
    "echo": False,
}

if DATABASE_URL.startswith("sqlite"):
    engine_kwargs["connect_args"] = {
        "check_same_thread": False,
    }

engine = create_engine(
    DATABASE_URL,
    **engine_kwargs,
)

Configuration:

from pathlib import Path
import os

from dotenv import load_dotenv

BASE_DIR = Path(__file__).resolve().parent.parent

load_dotenv(BASE_DIR / ".env")

DATABASE_URL = os.getenv(
    "DATABASE_URL",
    f"sqlite:///{BASE_DIR / 'trimly.db'}",
)

The SQLite database lives inside:

backend/trimly.db

Another issue I hit

After editing database.py I accidentally commented out the definition of engine_kwargs, which caused:

NameError: name 'engine_kwargs' is not defined

That was fixed by restoring the dictionary definition.

Right now when visiting docs, it keeps on reloading but nothing happens.

You need to follow https://help.pythonanywhere.com/pages/ASGICommandLine