Contents

Chapter 1

Chapter 1: Getting Started & Project Setup

Multi-tenant Django foundation with Stripe billing, custom user model, and production-hardened settings β€” launch your SaaS in days.

What You Get

  • Custom User model β€” Email-based auth with roles, built from AbstractBaseUser
  • Multi-tenant architecture β€” Subdomain-based tenant resolution with shared database
  • Stripe billing β€” Plans, subscriptions, invoices, and webhook handling
  • Production settings β€” Whitenoise, S3 storage, Sentry, secure cookies, HSTS
  • REST API β€” Django REST Framework with JWT auth and OpenAPI docs
  • Docker-ready β€” Gunicorn, PostgreSQL, Redis, Celery worker
  • HTMX + Alpine.js β€” Modern server-rendered frontend without SPA complexity
  • Split settings β€” Base / development / production configuration pattern

File Tree

django-saas-boilerplate/
β”œβ”€β”€ config/
β”‚   β”œβ”€β”€ settings/
β”‚   β”‚   β”œβ”€β”€ base.py              # Shared Django settings
β”‚   β”‚   β”œβ”€β”€ development.py       # Dev overrides
β”‚   β”‚   └── production.py        # Production hardening
β”‚   └── urls.py                  # Root URL configuration
β”œβ”€β”€ apps/
β”‚   β”œβ”€β”€ accounts/
β”‚   β”‚   β”œβ”€β”€ models.py            # Custom User model
β”‚   β”‚   β”œβ”€β”€ serializers.py       # DRF serializers
β”‚   β”‚   β”œβ”€β”€ views.py             # Auth & profile ViewSets
β”‚   β”‚   └── urls.py              # Account routes
β”‚   β”œβ”€β”€ tenants/
β”‚   β”‚   β”œβ”€β”€ models.py            # Tenant & plan models
β”‚   β”‚   └── middleware.py        # Subdomain β†’ tenant
β”‚   └── billing/
β”‚       β”œβ”€β”€ models.py            # Subscription & Invoice
β”‚       └── webhooks.py          # Stripe webhook handler
β”œβ”€β”€ templates/
β”‚   └── base.html                # HTMX + Alpine.js base
β”œβ”€β”€ docker/
β”‚   β”œβ”€β”€ Dockerfile               # Multi-stage Django build
β”‚   └── docker-compose.yml       # Full stack
β”œβ”€β”€ manage.py
└── manifest.json

Requirements

  • Python 3.11+
  • PostgreSQL 15+
  • Redis 7+ (for Celery task queue)
  • Stripe account (for billing features)
Chapter 2

Chapter 2: Configuration & Running

The boilerplate separates settings by environment so local convenience never weakens production security. config/settings/base.py contains shared apps, middleware, REST Framework, PostgreSQL, Redis, Stripe, and path settings. development.py imports the base module and enables debug tooling; production.py adds secure cookies, HSTS, Whitenoise, S3, Redis caching, and Sentry. This explicit import pattern provides the same benefit as django-split-settings without another dependency. Set the module when starting Django:

bash
export DJANGO_SETTINGS_MODULE=config.settings.development
python manage.py runserver

# Production processes use:
export DJANGO_SETTINGS_MODULE=config.settings.production

Environment variables

Copy .env.example to .env, keep it out of version control, and provide at least SECRET_KEY, database credentials, Stripe keys, and REDIS_URL. The supplied settings use os.environ; teams that prefer typed values can add django-environ (or use equivalent python-decouple calls):

python
# config/settings/base.py
import environ

env = environ.Env(DEBUG=(bool, False))
environ.Env.read_env(BASE_DIR / ".env")  # local only

SECRET_KEY = env("SECRET_KEY")
DEBUG = env.bool("DEBUG")
DATABASES = {"default": env.db("DATABASE_URL")}
STRIPE_SECRET_KEY = env("STRIPE_SECRET_KEY", default="")

Never give production secrets a usable default. In hosted environments, inject them through the platform’s secret manager rather than shipping an .env file.

Lists need deliberate parsing. For example, split ALLOWED_HOSTS and CORS_ALLOWED_ORIGINS on commas, strip whitespace, and reject empty production values. Boolean strings also need typed conversion: Python treats the nonempty string "false" as true. Fail during startup when a required value is absent, so a broken release never reaches customer traffic.

Database configuration

PostgreSQL is the production target and the default bundled backend. For local parity, run PostgreSQL through Docker and use DB_NAME, DB_USER, DB_PASSWORD, DB_HOST, and DB_PORT. SQLite is acceptable for quick UI work, but it will not expose PostgreSQL-specific locking, indexing, or concurrency behavior:

python
# config/settings/development.py
if os.environ.get("USE_SQLITE") == "1":
    DATABASES = {
        "default": {
            "ENGINE": "django.db.backends.sqlite3",
            "NAME": BASE_DIR / "db.sqlite3",
        }
    }

Run migrations whenever models or branches change: python manage.py migrate. Use PostgreSQL in CI before merging database-sensitive work.

Static files and uploads

STATIC_ROOT receives collected assets; MEDIA_ROOT stores local uploads during development. In production, Whitenoise serves versioned static assets from the application image, while django-storages sends private user uploads to S3:

python
MIDDLEWARE.insert(1, "whitenoise.middleware.WhiteNoiseMiddleware")
STATICFILES_STORAGE = (
    "whitenoise.storage.CompressedManifestStaticFilesStorage"
)
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
AWS_DEFAULT_ACL = "private"

Run python manage.py collectstatic --noinput during the image build. Do not serve customer uploads through Whitenoise; use short-lived signed S3 URLs.

Local and production processes

For a native setup, install requirements, start PostgreSQL and Redis, migrate, then run python manage.py runserver. The bundled Compose stack is closer to production:

bash
docker compose -f docker/docker-compose.yml up --build -d
docker compose -f docker/docker-compose.yml exec app python manage.py migrate
docker compose -f docker/docker-compose.yml exec app python manage.py createsuperuser

runserver is development-only. The Docker image should start Gunicorn behind Nginx, Cloudflare, or the hosting load balancer:

bash
gunicorn config.wsgi:application --bind 0.0.0.0:8000 \
  --workers 3 --timeout 60 --access-logfile -

Before routing traffic, run python manage.py check --deploy, apply migrations as a release step, collect static files, and verify /admin/, API authentication, Redis/Celery, and the Stripe webhook endpoint. Terminate TLS at the proxy and forward X-Forwarded-Proto; the production settings already enforce HTTPS and secure cookies.

Chapter 3
πŸ”’ Available in full product

Chapter 3: Architecture & Testing

You’ve reached the end of the free preview

Get the full Django SaaS Boilerplate and unlock everything.

All Chapters

Get the complete guide with every chapter unlocked, including code samples, diagrams, and best practices.

Full Tool Suite

Access all interactive tools with complete data, all workload profiles, and the full scenario library.

Source Files

Downloadable source code, configuration files, and working examples from every chapter.

Lifetime Updates

Free updates for life. Every new chapter, tool, and improvement included.

Buy Now — $59 →
πŸ“¦ Free sample included — download another copy or visit the store for the full product.
Django SaaS Boilerplate v1.0.0 β€” Free Preview