stack / 05 — updated may 2026

Build a Course / Coaching platform for $100/mo

Lessons, lesson videos, paid enrollment, cohort emails, scheduled calls. Video is the line item that ruins most $50 budgets — this stack keeps it civil.

$78 is the recurring monthly cost; transactional Stripe fees (2.9% + 30¢ per sale) are on top. The Mux line item assumes a typical small-course catalog of 30–50 videos with steady-but-modest traffic.

cap$100/mo
actual$78/mo
updatedMay 2026
services7 · 3 free
stack.txt — receipts/course-100.mdmonthly
SERVICENOTESUSD
Next.js / Vercelsite + lesson playerfree
Mux Pay-as-you-govideo hosting + streaming$30
Clerk Proauth + organizations$25
SupabasePostgres + storagefree
Stripepayments (no MoR)free
Resendtransactional + drip email$20
Cal.comscheduled calls + 1:1 coaching$3
SUBTOTAL · monthly7 services · 3 free tiers$78/mo
>cheapstack — runs in production. jump to setup →
why / the picks

Why this stack

Course platforms have one expensive line item: video. Everything else is solved cheaply. The trick is to pay for video correctly — on a per-minute usage basis, not on a flat-rate platform that markets itself to course creators with hidden caps.

Mux is the cheapest fair option for legitimate video hosting at this scale. Self-hosting on S3 is cheaper on paper but the moment you encode for adaptive streaming, build a player, handle mobile network conditions, and pay egress, you’ve recreated Mux poorly. Cloudflare Stream is the comparable alternative; price is similar.

The reason to pay Clerk Pro instead of staying on free: Organizations support unlocks cohort-based courses, and custom-domain auth keeps the brand intact. Both matter for paid education products where the brand is half the value proposition.

The honest tradeoff: Stripe’s zero-monthly fee is great until you have international customers, at which point sales tax becomes your problem. The course example here assumes US-only or US-primary; if your audience is significantly EU/UK, swap Stripe for Stripe Tax (+$50) or Lemonsqueezy (built into the rate). Don’t skip this calculation.

services / 7 picks

Per-service deep dives

01

Next.js / Vercel

free Updated May 2026
What it does

Hosts the marketing site, the enrolled-learner experience, and the embedded video player on a single Next.js app.

Why we picked it

Course content sites are mostly reads with bursts of writes (signups, completion tracking, comments). Vercel's edge handles the read path at zero cost, and serverless functions scale fine for the writes. Hobby tier is enough until you take your first paid customer; commercial use bumps you to Pro at $20/mo, which absorbs into the $100 cap easily.

Where it might bite you
  • Hobby's commercial-use ToS is a non-starter for paid courses. The day you sell access, you owe Vercel $20 — bake it into pricing from the start.
  • Long-running serverless functions (e.g., a video-completion webhook handler doing analytics) can hit the 10s timeout. Push that work to a queue (Upstash QStash or Cron job) instead.
  • Image optimization is great until you hit Vercel's transform budget. Serve large hero images from Cloudflare R2 if your marketing pages are image-heavy.
02

Mux Pay-as-you-go

$30/mo Updated May 2026
What it does

Encodes uploaded videos to adaptive bitrate, streams them through a global CDN, and reports playback analytics.

Why we picked it

Self-hosting video is cursed: encoding pipelines, CDN bills, mobile-network adaptive streaming, DRM if you ever need it. Mux does all of this and prices fairly: about $0.04 per minute of encoded video stored, and $0.0006 per minute streamed. For a typical small course (30 lessons × 15min average, 50 monthly enrollments watching most lessons), the monthly bill lands around $30. The player drops in as a React component.

Where it might bite you
  • Encoding is one-time but the storage cost is monthly. Don't re-encode the same lesson on every content tweak — diff carefully.
  • Streaming costs scale with traffic. A viral course launch can spike the bill 10x in a week. Set Mux's billing alerts on day one.
  • Mux doesn't do DRM at the entry tier. If your courses include premium content that needs encryption, the next tier is significantly more expensive.
03

Clerk Pro

$25/mo Updated May 2026
What it does

Drop-in auth with prebuilt UI, plus Organizations support for cohort-based courses where learners belong to a group.

Why we picked it

Pro at $25 unlocks custom-domain auth pages (you sign in at app.yourcourse.com instead of clerk.yourcourse.com), which matters for brand. Organizations support — included on Pro — handles cohort-based courses where 10 students are enrolled together by a company. The free tier doesn't have either, and rolling your own auth + orgs is a multi-week distraction from teaching.

Where it might bite you
  • Per-MAU pricing kicks in past 10k MAU on Pro — at scale Clerk gets expensive. Watch the math at 5k learners.
  • Custom email templates require some customization work. Clerk's defaults are professional but generic; spend a day matching them to your brand voice.
  • If you use Organizations, every learner needs to be in one (even solo learners). Build a 'personal workspace' org auto-creation flow.
04

Supabase

free Updated May 2026
What it does

Stores course structure, lesson metadata, enrollment records, completion tracking, comments, and any non-video assets (PDFs, slides).

Why we picked it

Free tier is genuinely free for a small course catalog: 500MB DB, 1GB file storage, 50k MAU. Course data is small (lessons rarely have more than a few KB of metadata each), and the free tier covers it through several hundred enrolled students. Auth lives on Clerk so we don't use Supabase Auth — pure DB + Storage.

Where it might bite you
  • Free tier pauses after 7 days of inactivity. For a course site that gets daily traffic this isn't an issue, but a quarterly paid course with no traffic between cohorts will hit it.
  • Connection pooling is on the free tier but tighter than Pro's. Heavy serverless fan-out can saturate it; if logged-in pages start timing out, that's the symptom.
  • RLS policies are silently wrong by default — start tests against the anon and authenticated keys, not just the service-role key.
05

Stripe

free Updated May 2026
What it does

Accepts card payments for course enrollments and recurring subscriptions, with no monthly platform fee.

Why we picked it

Stripe has zero monthly fee — you pay 2.9% + 30¢ per transaction. For courses priced at $99–$499, that's $3.20–$15 per sale. Lemonsqueezy (Merchant of Record) takes 5% + 50¢, which is more — but Stripe makes you handle sales tax. For US-only courses with nexus in one state, Stripe + your accountant works. For broad international, Lemonsqueezy or Stripe Tax becomes the right call.

Where it might bite you
  • Sales tax is your problem, not Stripe's, unless you add Stripe Tax ($50/mo + 0.5% per tx). At small course volume the manual approach is fine; at $5k+/mo MRR add Stripe Tax.
  • Webhook event handlers must be idempotent. Stripe sometimes delivers the same event twice; non-idempotent code creates duplicate enrollments.
  • Subscription dunning (failed renewal recovery) is built into Stripe but generic. For a course with strong unit economics, custom dunning emails recover an extra 5-10% of failed payments.
06

Resend

$20/mo Updated May 2026
What it does

Sends enrollment confirmations, drip cohort sequences, lesson-completion notifications, and weekly digests.

Why we picked it

$20 covers 50k emails — large enough for any small-to-medium course operation. Drip sequences for cohorts (Day 1: welcome, Day 7: midway check-in, Day 30: completion) are first-class with the React-email integration. Same provider as the SaaS stack, same templates, no learning curve.

Where it might bite you
  • Resend is transactional-first; broadcast email (newsletters, course-launch emails to non-students) belongs on Beehiiv. Don't try to use Resend for marketing — the team enforces the distinction.
  • Drip sequences need a worker or a cron job to schedule sends. Resend doesn't have a built-in scheduler beyond simple sends. Use a Render cron job or QStash.
  • Domain verification (SPF, DKIM, DMARC) is unforgiving. Set them up before launch; after launch, deliverability complaints are real-customer complaints.
07

Cal.com

$3/mo Updated May 2026
What it does

Lets learners book office hours, 1:1 coaching, or live Q&A sessions on a calendar that integrates with your real calendar.

Why we picked it

Coaching platforms need bookings. Calendly is fine but $12/mo per user; Cal.com's free tier covers most needs and Pro at $3/mo unlocks workflows (auto-reminders, custom branding, Zapier integrations). If your course includes 1:1 calls, a 'coaching' tier, or office hours, Cal.com pays for itself.

Where it might bite you
  • Cal.com's free tier hosts at cal.com/yourname; Pro lets you embed at yourcourse.com/book or similar. Course brand consistency benefits from Pro.
  • If you use Google Calendar's free tier, two-way sync sometimes lags. Critical bookings should be confirmed via email even if the calendar shows them.
  • Automated reminders (24h, 1h) reduce no-shows by 20%+. They're a Pro feature; turn them on day one.
setup / ~30 minutes

Setup in 30 minutes

Two days from blank repo to first paid enrollment. The longest single block is video encoding the first batch of lessons; everything else is wiring.

  1. Bootstrap Next.js, push to Vercel, point custom domain

    npx create-next-app@latest with the App Router. Add a marketing landing page, deploy to Vercel, attach custom domain. Make sure HTTPS is live before any other step.

  2. Set up Clerk with Organizations enabled

    Create Clerk app with Pro tier from day one (you'll need it). Enable Organizations in the dashboard. Wire <SignIn />, <UserButton />, and <CreateOrganization /> into the app's auth pages. Test with two test users and one org before moving on.

  3. Connect Supabase for course data

    Create Supabase project (free tier). Define schema: courses, lessons, enrollments, lesson_completions, comments. Use the service-role key from a Next.js server action, expose anon-key reads via RLS policies tied to Clerk's user_id metadata.

  4. Wire Mux for video upload + playback

    Mux account → API keys → server-side upload from your admin panel using direct uploads. Lessons table stores the Mux playback ID. Embed @mux/mux-player-react in lesson pages with auth-gated playback URLs.

  5. Configure Stripe for one-time and subscription payments

    Stripe account, products and prices for each course or pricing tier. Webhook endpoint listens for checkout.session.completed to grant access; customer.subscription.deleted to revoke. Test mode end-to-end before live keys go in.

  6. Wire Resend for transactional + drip sequences

    Verify sending domain. Templates (react-email): welcome, lesson-reminder, completion-cert, drip Day-1/Day-7/Day-30. A Vercel Cron route fires daily to advance enrollees through the drip sequence based on enrollment_date.

  7. Drop in Cal.com for office hours

    Cal.com Pro account, create a 30-min event type for office hours, embed booking widget on the in-course /office-hours page (auth-gated). Connect to your Google Calendar so blocks show across both.

thresholds / numeric

When this stack runs out

The thresholds are mostly about traffic and content volume; payment volume scales infinitely on Stripe.

$200/mo Mux bill
Sustained video bills above $200 are the signal that pay-as-you-go isn't the cheapest option. Mux Plus tier ($1k flat, much higher caps) becomes worth it. At lower volume, stay PAYG.
10k MAU on Clerk
Per-MAU pricing kicks in. The next dollar adds to the $25 base. Watch the curve and consider Auth.js if margins thin.
Supabase free tier exhaustion
500MB DB or 1GB storage exhausted, or 7 days of inactivity pause. Pro at $25/mo is the next stop and removes pausing entirely.
$5k MRR with international customers
Sales tax compliance becomes meaningful. Add Stripe Tax (+$50/mo) or migrate payments to Lemonsqueezy. Both are correct; pick by team capacity.
50k emails/mo
Resend's $20 tier ends. Bump to $35/mo or split marketing onto Beehiiv.
gotchas / from production

Common gotchas

Course-platform footguns. Most of these are about the gap between “working” and “working at 2am when a learner’s payment fails on a sale day.”

  • VIDEO

    Mux billing spikes during launch week

    A successful course launch can 10x video streaming overnight. Set Mux billing alerts on Day 1 — at $50, $100, $200 thresholds — so you see the spike instead of a $1,400 invoice.

  • AUTH

    Clerk Organizations are required for Org-based features

    If you enable Organizations, every user must be in at least one. Auto-create a 'personal' org on signup, or some users get stuck on a 'select your organization' screen with no orgs to select.

  • PAYMENTS

    Stripe webhooks must be idempotent

    Stripe sometimes delivers the same event twice. Without idempotent handlers, learners can be enrolled twice (and access twice). Track event IDs in a processed-webhooks table.

  • DRIP

    Cron-based drip sequences need timezone awareness

    A 'Day 7 check-in' email sent at server-time UTC arrives at 4am for Pacific learners. Schedule based on the learner's timezone (Clerk stores it) or send during a window like 09:00 local.

  • DB

    Supabase free tier pauses after 7 idle days

    If your course has off-cohort weeks with no traffic, the DB pauses and the first request after takes 30+ seconds to wake. Either keep traffic warm with a daily cron-ping, or upgrade to Pro before the pause becomes a learner-facing problem.

  • TAX

    Sales tax accumulates silently

    Without Stripe Tax, you collect from learners but the tax filings are your responsibility. After a quarter you owe the state. After a year you owe the IRS. Set up tax tracking on Day 1 even if you don't think you need it.