My Exact Setup for Building a Scalable Python Web App 🚀
From folder structure to deployment, here’s the real-world stack I use to build and scale modern Python web apps.

I’ve rebuilt this stack enough times to know what works — and what quietly falls apart at scale.
My Exact Setup for Building a Scalable Python Web App 🚀
Building a Python web app is easy.
Building a scalable, maintainable, production-grade app? That’s a different game.
In this post, I’ll walk you through my exact setup — the tools, architecture, and practices I use to build Python web applications that are designed to scale from day one.
Whether you’re building a side project, startup MVP, or enterprise SaaS platform, this guide will give you a rock-solid foundation.
1. Project Structure: Start Clean or Die Debugging
“Good architecture is invisible — bad architecture yells at you through every bug.”
I follow a modular structure inspired by Domain-Driven Design (DDD) and the Hexagonal Architecture pattern. It keeps code decoupled, testable, and easier to scale as the project grows.
myapp/
├── app/
│ ├── api/ # Routes and request handling
│ ├── core/ # Config, app setup
│ ├── models/ # ORM models
│ ├── services/ # Business logic
│ ├── repositories/ # Database interaction
│ ├── schemas/ # Pydantic models
│ └── utils/ # Shared utilities
├── tests/ # Pytest-based test suite
├── alembic/ # DB migrations
├── .env
├── Dockerfile
├── pyproject.toml
└── README.md
This lets me separate concerns while keeping code readable.
2. Framework of Choice: FastAPI + Uvicorn + Pydantic
FastAPI has become my go-to for building APIs because it’s:
- Fast (async-first and performance-oriented)
- Typed (thanks to Pydantic)
- Intuitive (auto docs with Swagger/OpenAPI)
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
Behind the scenes:
- Uvicorn as the ASGI server
- Gunicorn for multi-worker support in production
- Pydantic for data validation
- Starlette for request/response layer
3. Testing: Pytest + FactoryBoy + HTTPX
“Code that’s not tested is broken by default.”
- Pytest for writing concise, readable tests
- HTTPX for async API testing
- FactoryBoy for generating fake data in unit tests
Example of a simple route test:
def test_health_check(client):
response = client.get("/health")
assert response.status_code == 200
assert response.json() == {"status": "ok"}
I also use coverage reports (pytest --cov
) and CI integration to avoid regressions.
4. Database: PostgreSQL + SQLAlchemy 2.0 + Alembic
- PostgreSQL is rock-solid, scalable, and works everywhere.
- SQLAlchemy 2.0 gives me the best of both ORM and raw SQL worlds.
- Alembic handles schema migrations cleanly.
I often wrap DB operations in repository classes so I can switch databases or mock behavior in tests.
5. Dependency Management: Poetry
Forget pip freeze
and requirements.txt
hell.
With Poetry, I manage packages and virtual environments like a pro:
poetry add fastapi
poetry shell
poetry run uvicorn app.main:app --reload
It also supports publishing packages and managing pyproject.toml
seamlessly.
6. Containers: Docker + Docker Compose
“If it runs on your machine but not on prod, it doesn’t work.”
My Dockerfile
is lean, production-ready, and compatible with gunicorn + uvicorn
workers:
FROM python:3.12-slim
WORKDIR /app
COPY pyproject.toml poetry.lock ./
RUN pip install poetry && poetry install --no-root
COPY . .
CMD ["poetry", "run", "gunicorn", "-k", "uvicorn.workers.UvicornWorker", "app.main:app"]
And docker-compose.yml
makes it easy to spin up the whole stack (API + DB + Redis + Celery):
services:
web:
build: .
ports:
- "8000:8000"
depends_on:
- db
db:
image: postgres:15
7. Deployment: Fly.io or Render for Small Projects, AWS/GCP for Scale
- Fly.io is my favorite for quick global deployment of small apps.
- Render makes CI/CD and DB hosting dead simple.
- AWS ECS / GCP Cloud Run for more control and enterprise scale.
I always include:
.env
+ secrets handling- Automatic SSL with HTTPS
- CI/CD using GitHub Actions
8. Background Tasks: Celery + Redis
Heavy lifting like:
- Sending emails
- Processing images
- Scheduled jobs
…goes to Celery workers. I run it alongside the main app using:
celery -A app.worker worker --loglevel=info
Redis acts as the broker and result backend.
9. Auth & Security: OAuth2 + JWT + CORS
- Use OAuth2 password flow for secure login
- JWT tokens for stateless auth
- Passlib for password hashing
- Enable CORS properly for frontend integrations
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
Security is baked in from day one — never an afterthought.
10. Monitoring: Sentry + Prometheus + Logs
- Sentry for error tracking and alerts
- Prometheus + Grafana for metrics
- Structured logging with
loguru
orstructlog
It’s easier to prevent outages than fix them.
Bonus Tools I Use Every Time
- Pre-commit Hooks (
black
,ruff
,mypy
,isort
) - Makefile for shortcut commands
- .env + python-dotenv for config
- Postman or Insomnia for API testing
- Swagger UI for automatic docs
Final Thoughts
You don’t need a huge team or a dozen microservices to build a scalable web app in Python. But you do need the right setup — one that lets you grow without rewriting your foundation.
This stack is battle-tested, minimal yet scalable, and easy to maintain.
Feel free to copy it, fork it, or remix it to fit your needs.
Got questions or want to see the boilerplate repo? Drop a comment or DM me. Let’s build something scalable!
If you enjoyed this, consider following me for more deep dives into Python, FastAPI, and backend architecture.
