DEV Community

Kirill Strelnikov
Kirill Strelnikov

Posted on • Originally published at kirweb.site

The Django SaaS MVP Stack I Use in 2026 (Ships in 4-6 Weeks)

I'm Kirill Strelnikov, a freelance Python/Django developer in Barcelona. I've built 5+ SaaS platforms with Django — from time-tracking systems for restaurant chains to multi-tenant platforms with Stripe billing. Here's the exact stack and architecture I use in 2026 to ship SaaS MVPs in 4-6 weeks.

The Stack

Django 5.x          → Web framework (admin, auth, ORM, REST)
PostgreSQL 16       → Primary database (with schema-per-tenant)
Redis               → Cache, sessions, Celery broker
Celery              → Background tasks, scheduled jobs
Django REST Framework → API layer
Stripe              → Subscription billing
Docker + Nginx      → Deployment
Gunicorn            → WSGI server
Enter fullscreen mode Exit fullscreen mode

This isn't theoretical. Every component here has been battle-tested across real projects with paying customers.

Why Django for SaaS (Still, in 2026)

I get asked "why not FastAPI?" or "why not Node.js?" constantly. Here's my honest take after building with all three:

Django gives you 60% of a SaaS for free:

  • Authentication + permissions (including social auth via django-allauth)
  • Admin panel (your first "back office" dashboard — free)
  • ORM with migrations (schema changes without SQL)
  • Form validation and CSRF protection
  • Session management
  • Built-in security middleware

With FastAPI, you build all of that yourself. FastAPI is great for microservices and high-throughput APIs. But for a SaaS MVP where you need auth, admin, billing, and a dashboard? Django saves 2-3 weeks of development.

I wrote a detailed comparison: Django vs FastAPI for SaaS

Multi-Tenant Architecture

For SaaS, you need tenant isolation. I use schema-per-tenant with django-tenants:

# settings.py
DATABASES = {
    "default": {
        "ENGINE": "django_tenants.postgresql_backend",
        "NAME": "saas_db",
    }
}

TENANT_MODEL = "customers.Client"
TENANT_DOMAIN_MODEL = "customers.Domain"

SHARED_APPS = [
    "django_tenants",
    "customers",       # Tenant management
    "django.contrib.admin",
    "django.contrib.auth",
]

TENANT_APPS = [
    "core",            # Your SaaS app
    "billing",
    "analytics",
]
Enter fullscreen mode Exit fullscreen mode

Each tenant gets their own PostgreSQL schema. Data isolation is guaranteed at the database level — no WHERE tenant_id = X scattered through your code.

Real example: I built a time-tracking SaaS for a chain of 5 cafes in Barcelona. Each cafe is a separate tenant with its own employees, shifts, and payroll data. Result: 80% reduction in payroll processing time across all locations.

Stripe Billing Pattern

Every SaaS needs billing. Here's my standard Stripe integration:

# billing/models.py
class Subscription(models.Model):
    tenant = models.OneToOneField(Client, on_delete=models.CASCADE)
    stripe_customer_id = models.CharField(max_length=255)
    stripe_subscription_id = models.CharField(max_length=255, blank=True)
    plan = models.CharField(
        max_length=20,
        choices=[("free", "Free"), ("pro", "Pro"), ("business", "Business")]
    )
    status = models.CharField(max_length=20, default="active")

# billing/webhooks.py
@csrf_exempt
def stripe_webhook(request):
    payload = request.body
    sig = request.META.get("HTTP_STRIPE_SIGNATURE")
    event = stripe.Webhook.construct_event(payload, sig, WEBHOOK_SECRET)

    if event.type == "invoice.paid":
        handle_successful_payment(event.data.object)
    elif event.type == "customer.subscription.deleted":
        handle_cancellation(event.data.object)

    return HttpResponse(status=200)
Enter fullscreen mode Exit fullscreen mode

I always use webhooks instead of polling. Stripe sends events in real-time — your app reacts immediately.

Background Tasks with Celery

SaaS apps need background processing: sending emails, generating reports, syncing data. Celery handles all of this:

# tasks.py
@shared_task
def generate_monthly_report(tenant_id: int):
    tenant = Client.objects.get(id=tenant_id)
    with tenant_context(tenant):
        data = aggregate_monthly_metrics()
        pdf = render_report_pdf(data)
        send_report_email(tenant.owner.email, pdf)

# Scheduled task (Celery Beat)
CELERY_BEAT_SCHEDULE = {
    "monthly-reports": {
        "task": "tasks.generate_monthly_report_all",
        "schedule": crontab(day_of_month=1, hour=6),
    },
}
Enter fullscreen mode Exit fullscreen mode

Deployment: Docker + Nginx + Gunicorn

# docker-compose.yml
services:
  web:
    build: .
    command: gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 3
    volumes:
      - static:/app/static
    depends_on:
      - db
      - redis

  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine

  celery:
    build: .
    command: celery -A config worker -l info

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - static:/app/static
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
Enter fullscreen mode Exit fullscreen mode

One docker compose up and your entire stack is running. Same on development, staging, and production.

Timeline: 4-6 Weeks to MVP

Week Deliverable
1 Project setup, multi-tenant config, auth, basic models
2 Core features (the thing your SaaS actually does)
3 Dashboard, admin panel, REST API
4 Stripe billing, subscription management
5 Deployment, monitoring, load testing
6 Buffer for feedback and iterations

Week 2 is the only week that varies between projects. Everything else is a repeatable pattern I've refined over 5+ SaaS builds.

Cost

Based on my project history:

Tier What you get Cost Timeline
Basic MVP Auth, core feature, admin, basic billing EUR 1,500-4,000 3-4 weeks
Standard Multi-tenant, Stripe, dashboard, API EUR 4,000-10,000 5-8 weeks
Enterprise Custom integrations, analytics, multi-region EUR 10,000+ 8-12 weeks

Detailed breakdown: SaaS MVP Development Cost

Common Mistakes I See

  1. Building auth from scratch. Use django-allauth. It handles email verification, social login, password reset, and 2FA. Don't reinvent this.

  2. Skipping multi-tenancy early. Adding tenant isolation to an existing codebase is painful. Start with django-tenants from day 1.

  3. No background tasks. Everything runs in the request cycle. First heavy operation = timeout. Add Celery from the start.

  4. Over-engineering the MVP. Your first version needs: auth, one core feature, billing, and a way to contact you. Ship that. Iterate after real users give feedback.


I'm Kirill Strelnikov — freelance Python/Django developer in Barcelona, Spain. I build SaaS platforms, AI chatbots, and Telegram bots. 15+ projects delivered across Europe.

Top comments (0)