# Multi-tenant SaaS for $30/mo: wildcard SSL + nginx vhost generator + Flask routing

> Source: <https://dev.to/guardlabs_team/multi-tenant-saas-for-30mo-wildcard-ssl-nginx-vhost-generator-flask-routing-597k>
> Published: 2026-05-23 21:00:03+00:00

# Multi-tenant SaaS for $30/mo: the actual architecture

Every "build a SaaS" tutorial reaches for Kubernetes, managed Postgres, and three cloud services before the first user signs up. You don't need that. Here's a multi-tenant setup that runs 50+ tenants on subdomains, on one $30/mo VPS.

## The requirement

White-label partnership program. Each partner gets a storefront at `partner-{slug}.guardlabs.online`

with our catalog, their referral IDs baked in. New partner → automatic provisioning after payment webhook.

## Phase 1 (0-50 tenants): wildcard SSL + nginx + Flask

**Wildcard SSL via Let's Encrypt** — one cert covers `*.guardlabs.online`

:

```
certbot certonly --manual --preferred-challenges dns \
  -d "*.guardlabs.online" -d "guardlabs.online"
```

(DNS-01 challenge — add the TXT record your DNS provider, certbot validates.)

**nginx server block** routing by `$host`

:

```
server {
    listen 443 ssl;
    server_name ~^partner-(?<partner_slug>.+)\.guardlabs\.online$;
    ssl_certificate     /etc/letsencrypt/live/guardlabs.online/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/guardlabs.online/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8090;
        proxy_set_header Host $host;
        proxy_set_header X-Partner-Slug $partner_slug;
    }
}
```

**Flask reads the header**, scopes everything to that partner:

``` python
@app.before_request
def load_partner():
    slug = request.headers.get("X-Partner-Slug")
    if slug:
        g.partner = db.execute(
            "SELECT * FROM partners WHERE slug=?", (slug,)
        ).fetchone()
        if not g.partner:
            abort(404)

@app.route("/")
def storefront():
    products = get_catalog()  # global catalog
    return render_template("store.html",
        products=products,
        ref_id=g.partner["ref_id"],  # injected into every buy link
        partner_brand=g.partner["brand_name"])
```

**Provisioning on webhook** — when Whop sends "new subscription":

``` python
@app.route("/webhook/whop", methods=["POST"])
def whop_webhook():
    event = verify_and_parse(request)
    if event["type"] == "subscription.created":
        slug = slugify(event["user"]["username"])
        # 1. DB row
        db.execute("INSERT INTO partners (slug, ref_id, ...) VALUES (...)")
        # 2. nginx — no per-partner config needed! Regex server_name catches it.
        # 3. Done. partner-{slug}.guardlabs.online works immediately.
    return "", 200
```

That's the trick: **the regex server_name means zero new nginx config per partner.** Wildcard SSL means zero new certs. The DB row is the only write.

**Capacity:** one VPS, monolith Flask, SQLite. ~50 partners with 10 concurrent users each = 500 concurrent. A $30/mo Hetzner CCX handles that without breathing hard.

## Phase 2 (50-200 tenants): per-tenant SQLite

Move from shared DB to `/data/partners/{partner_id}/store.db`

. Flask switches connection by `X-Partner-Slug`

. ~16 hours of refactor. SQLite holds 10k+ rows per file fine; 200 partners × 10k = 2M rows total, no problem.

## Phase 3 (200-500+): PostgreSQL schema-per-tenant

Now you migrate. One Postgres, schema per partner. 40 hours. *Now* you might want Kubernetes. Not before.

## The point

I provisioned this whole thing in about 8 hours. Total infra budget for 12 months: under $5K. The "you need microservices and managed everything" advice is for companies with funding and a platform team. For a solo founder: nginx regex + wildcard SSL + a Flask `before_request`

hook gets you to 50 paying tenants.

## Try it

The partnership it powers: 7-day free trial, no card, $29/mo. [guardlabs.online/partner](https://guardlabs.online/partner/). And the bot the whole thing grew out of: Phantom paper-trader (384 trades, 57% win-rate, public).

## Question

What's your "we over-engineered this" story? When did you reach for Kubernetes before you needed it?

*Code and full launch log in public. Following along.*

### 📥 Free chapter — 20 no-budget growth tactics

This launch log runs on a playbook. If you want the actual tactics — Google-ecosystem hacks, trend-jacking, the HARO authority play — [grab two free sections of the Blueprint](https://guardlabs.online/free-pdf/?utm_source=blog&utm_medium=post&utm_campaign=buildinpublic). No PDF wall, no login: it opens in your browser. Real numbers, real code, no fluff.
