# CCL Platform Infrastructure: live video and storage on our own servers

> This is the backend that CCL, Schoolaris and Polypro share for live classes, recordings and course files. It runs LiveKit video with four recording workers, multi-tenant S3 storage on MinIO, and signed, expiring links for streaming. Each platform gets live classes inside its own pages, recordings that come back as replays, and files that are scanned before they're stored.

I built and maintain it as full-stack developer at CCL, on the company's own servers.
Online at https://ccl.ma/
Canonical: https://salaheddine-elfatimi.com/projects/ccl-platform-infrastructure
Built with: TypeScript, React, Bun, Elysia, Prisma, MySQL, Redis, MinIO, LiveKit, Docker, Caddy, Dokploy, ClamAV, FFmpeg

## The client

CCL (Centre de Compétences et Leadership) is a training academy in Marrakech. At CCL I build and maintain several e-learning platforms, including CCL's own sites, Schoolaris, an online tutoring platform for Moroccan pupils, and Polypro Academy, a language-learning platform. CCL, Schoolaris and Polypro all have live video built in, so all three needed video, recording and storage.

## What they needed

CCL's live classes used to run on Zoom links, with recordings kept in the meeting tool instead of next to the student's programme. The goal was to bring classes inside the platforms, with video and recordings on the company's own servers rather than a per-minute service.

Self-hosting live video takes more than one server. Recording is heavy: each recording runs a full headless Chrome, and one worker carried about 5 to 8 before the host struggled. A recorded class takes about 1 GB per hour, uploaded files need checking before they're stored, and videos have to stream through links that don't stay open forever.

## What I built

- **LiveKit behind Caddy, with TURN**: The LiveKit server, Redis and TURN run in Docker, with Caddy handling HTTPS. TURN over TLS lets students on strict school, office or mobile networks still join, and our API creates the rooms and signs a short-lived token for each student.
- **Four recording workers**: Recordings run on four LiveKit Egress workers that share one Redis queue, each with its own temp folder. A shared folder once made the workers delete each other's sockets, which looked like a capacity problem but wasn't.
- **A cap set by a load test**: In a May 2026 load test on the 80-core production host, 24 recordings at once used about half the CPU with every file complete; 30 worked but left no margin. So the API won't auto-start a 25th recording, never stops one in progress, and admins can override it by hand.
- **Rooms that survive a dropped connection**: Rooms are created by the API, not on join, and stay open 15 minutes for the first person to arrive and 5 minutes after the last one leaves. A teacher's Wi-Fi dropping out doesn't end the class or its recording.
- **Multi-tenant storage on MinIO**: Each platform's course files and recordings go into S3 storage on MinIO. ClamAV checks every file before it's stored, and videos are transcoded to adaptive HLS for streaming.
- **Signed, expiring streaming links**: Videos and files reach students through signed links that expire, instead of permanent public URLs.

## What it lets them do

**Live classes, recordings and course files run on servers we control: Dockerized behind Caddy auto-HTTPS, with ClamAV checking files before they're stored and videos transcoded to adaptive HLS.**

CCL, Schoolaris and Polypro run live classes inside their own platforms on one shared setup, instead of sending students to meeting links. Recordings land in storage the company controls and come back as replays. The load test gives a known limit, 24 recordings at once, that only holds for this hardware: change the server, and the test has to run again.
