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
-
a2wsgi is installed
python -c "import a2wsgi; print(a2wsgi.file)"
works successfully.
-
FastAPI imports correctly
cd ~/trimly/backend
python -c "from app.api import app; print(app)"
returns a FastAPI instance.
- 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.