Caliber is a precision-sports analytics SaaS we built end-to-end: multi-tenant accounts, video ingestion, a scoring engine that processes uploaded shooting sessions into ranked performance data, a Vue.js dashboard, and a billing integration. It runs on Laravel. This post is a honest account of why.
What we were optimizing for
Four things, in priority order:
- Time to first paying customer. The business needed to validate the market fast. Anything that slowed us down by more than a few days was a problem.
- Background job throughput. Every uploaded session triggers a processing pipeline. Video transcode, frame analysis, metric extraction, database writes. The system had to handle bursts without melting.
- Multi-tenancy without compromise. Data isolation between customer accounts had to be correct the first time. We didn't want to paper over it with application-level checks.
- A team that could run it. One full-stack lead, one senior engineer, eventually a second. The stack had to be productive for a small team.
What we looked at
Node/Express + Prisma. Fast to prototype, but the ecosystem for queue processing, authentication, and admin tooling was fragmented. We'd be gluing together five libraries to get what a mature framework gives you for free.
Django. Strong ORM, great admin, battle-tested. The Python job queue story (Celery + Redis) works but isn't as clean as Laravel's. And we had more recent Laravel shipping experience than Django.
Ruby on Rails. Excellent framework, great developer experience. We'd have been fine here. The real reason we didn't: the talent pool we'd be hiring into long-term is bigger for Laravel, and the deployment tooling around Rails (at least in 2025 when we picked) had started to feel creaky without meaningful upside.
Laravel + Vue. Our final choice. Mature, opinionated, with first-class solutions to every problem we knew we'd hit.
The Laravel features that actually mattered
Most framework comparisons list features nobody uses. Here's what earned its keep in production.
Queues + Horizon
Caliber ingests video. Every upload fires a chain of background jobs — transcode to our canonical format, extract frames at the scoring intervals, run analysis, persist results, notify the client. These all need to run reliably, retry on failure, and never drop a user's session.
Laravel's queue abstraction over Redis gave us this cleanly. ShouldQueue, job chaining, exponential backoff, failed-job tables — all built in. Horizon gives us a dashboard to watch workers, tune concurrency, and see failure rates without building our own monitoring.
For a queue-heavy application, Laravel is genuinely excellent. We've yet to find a Node.js queue library that feels as solid.
Eloquent for the scoring model
Our data model has real relationships: an Organization has Teams, Teams have Athletes, Athletes have Sessions, Sessions have Rounds, Rounds have Shots, Shots have Metrics. Queries cross three or four boundaries constantly.
Eloquent with eager loading (with()) and lazy eager loading (load()) let us express these queries declaratively. The query debugger (Telescope, more below) made it obvious when we'd accidentally created an N+1. We never had to drop to raw SQL for application-level work.
Sanctum for SPA auth
Our Vue frontend talks to the Laravel API from the same top-level domain. Sanctum with cookie-based SPA authentication handled this with about fifty lines of configuration. No OAuth dance, no token refresh logic, no manual CSRF gymnastics. It just works.
If Caliber were mobile-first, we'd use Sanctum's token mode instead. The same package handles both with different middleware.
Telescope in staging
Telescope is the internal debug panel — every query, every cache hit, every queued job, every outgoing HTTP request, logged with timing. We run it in staging, never in production.
Two things it's saved us from: N+1 queries we'd have shipped without noticing, and third-party API calls timing out in patterns we couldn't see in logs alone. Worth the install every time.
Policies and gates
Multi-tenancy in Caliber means every read and write needs a tenant check. Laravel's authorization system puts this in one place: a Policy class per resource, referenced by one-liner authorize() calls in controllers.
We paired this with a global tenant() scope on Eloquent models. Every query is automatically filtered by the current user's organization unless explicitly bypassed. Data leakage would require a developer to go out of their way to create it.
What we built
The architecture at a high level:
- Laravel 10 API (now 11) serving JSON to the Vue SPA
- Vue 3 + Pinia for the dashboard, Vite for the build
- MySQL 8 as the primary data store, Redis for queues and cache
- S3 for video uploads, MediaConvert for transcoding pipelines
- Stripe billing via Cashier
- Deployed to AWS via Laravel Forge for API servers, separate worker instances for queues
The API stayed small. About 40 Eloquent models, 30 controllers, 20 policies, 25 queued jobs. Nothing exotic — the productivity came from Laravel giving us well-trodden paths for every problem we hit.
The gotchas
Being honest about where the stack cost us time.
Horizon on Kubernetes was annoying. We initially ran everything on ECS, and Horizon's process supervisor didn't play nicely with container lifecycle management. We ended up on Forge-managed EC2 for workers, which is less trendy but more reliable. If you're committed to Kubernetes for everything, factor in the integration work.
Eloquent is tempting to overuse. The ergonomics are so good that you can end up with 20 database queries for a single page render without noticing. Telescope surfaces this, but the discipline has to come from code review. For high-frequency endpoints, we dropped to explicit query builders or raw SQL where Eloquent was doing too much.
The "Laravel ecosystem" lock-in is real. Forge, Vapor, Nova, Horizon, Telescope, Cashier, Sanctum — they're all Laravel-branded products that work beautifully together and nowhere else. That's great until you want to move off Laravel. We don't plan to, but if your organization has a strong preference for vendor-neutral infrastructure, you should know.
Would we choose it again?
Yes. For a small team building a production SaaS with a known-moderate load profile, Laravel is the most productive stack we've used. The features line up with the problems SaaS actually hits — auth, jobs, billing, admin tooling, multi-tenancy — and the framework's opinions are mostly the right opinions.
Where we'd think harder:
- If we were building a real-time product (chat, collaborative editing, live dashboards), we'd want a Node runtime for the websocket layer and might use Laravel only for the API. Laravel Reverb changes this calculus somewhat, but we haven't shipped with it yet.
- If the team was Python-heavy, Django is a completely reasonable alternative.
- If we were a one-person build, we might go even lighter — a framework like Remix or Next.js with a minimal backend.
For the Caliber profile — multi-tenant SaaS, job-heavy, small team, real revenue — Laravel was the right call. Three years in, it still is.
If you're evaluating Laravel for a SaaS build and want to talk through your architecture, get in touch.
