All projects

LMS Platform

Live · self-hosted

A course-selling platform — instructors create courses with chapters and video lessons and set a price; students browse a catalog, pay, and track progress. Three roles: student, instructor, admin.

Open Live Demo Self-hosted on AWS EC2 · HTTP only — see the note below.
Why this link is HTTP, not HTTPS This deployment runs on a bare AWS EC2 IP without a domain or TLS certificate, so your browser may flag it "Not secure." That's expected for a self-hosted box, not a bug — it's actually the point of this case study: the same codebase also deploys to Vercel with HTTPS out of the box, and the contrast between the two is exactly what §3 below is about. The course catalog is publicly browsable with no login needed.

1 The Problem

Most course platforms are demoed on a managed host and nowhere else — which answers "can you build a feature" but not "can you own the infrastructure it runs on." The goal here was to build a real product (courses, payments, video, roles) and then prove it could ship two different ways from the same source: a zero-ops managed platform, and a box you configure and operate yourself.

2 The Approach

Content and access are strictly separated from delivery. Course structure (chapters, lessons, ordering) lives in Postgres via Prisma. Video never touches the app's own storage — it's uploaded straight to Mux, and playback is gated by signed tokens: an unenrolled user cannot obtain a working video URL at all, not even by guessing one. Payments run through Stripe Checkout, and the webhook handler verifies every signature and is written to be idempotent, so a replayed event can never double-grant access to a course.

3 Architecture — one codebase, two production deployments

The same repository ships to production two different ways simultaneously, sharing the same external services (Neon, Clerk, Stripe, Mux, Blob) but running on entirely different infrastructure:

Path A
Vercel
Git push → managed build → HTTPS out of the box, zero server ops.
Path B
AWS EC2 (self-hosted)
GitHub Actions → Docker build → push to Amazon ECR → SSH deploy → nginx reverse-proxy on the box.

The CI/CD pipeline for Path B: typecheck → build → push image to ECR (tagged :latest and :<sha>) → SSH into EC2 → pull, restart the container, prune old images → smoke-check the public URL. Every image keeps its commit SHA, so rollback is one docker run away.

GitHub Actions CI/CD pipeline: build, push to ECR, deploy to EC2 — green run on main

The actual pipeline run from this repository's GitHub Actions.

4 Tech Stack

Next.js 16 (App Router, RSC, Server Actions) React 19 TypeScript strict Tailwind + shadcn/ui Prisma + Neon Postgres Clerk (auth + roles) Stripe Checkout + webhooks Mux (signed playback) Vercel Blob Docker AWS EC2 + ECR GitHub Actions Zod Vitest

5 Result

A working course marketplace with real payment integrity and video protection, deployed and operating on two independent production environments from a single codebase.

2Independent production deploys
3User roles enforced server-side
100%Video gated by signed tokens
SHATagged images for instant rollback