TL;DR: Kumix.cloud is a multi-tenant SaaS dashboard built with Laravel and React that lets DevOps engineers, system administrators, and hosting providers manage VPS instances, SSH credentials, and server health from a single, real-time interface. Here’s how I built it.
The Problem
Managing multiple VPS servers across different providers is a fragmented nightmare. You bounce between:
- Provider dashboards (DigitalOcean, Vultr, Linode)
- SSH terminals for testing connections
- Spreadsheets tracking SSL certificate expiration dates
- Multiple SSH key backups and management tools
I needed one place to:
- Store and manage SSH keys securely
- Monitor server health in real-time
- Track SSL certificate expiration
- Test SSH connectivity instantly
- Scale to hundreds of servers without breaking a sweat
Most existing solutions were either bloated enterprise tools or bare-bones CLIs. I wanted something in between: powerful, intuitive, and cloud-native.
Architecture: Multi-Tenant SaaS from Day One
I designed Kumix.cloud as a multi-tenant platform from the ground up. This wasn’t just about supporting multiple users—it was about building the right foundation.
Core Principles
1. Security First
- SSH keys encrypted with AES-256 at rest
- Laravel Policies for granular authorization
- Every API endpoint enforces user ownership
- Rate limiting to prevent abuse
2. Real-Time Updates
- WebSocket broadcasts for instant metric updates
- Async job processing via queue workers
- No polling, no stale data
- Event-driven architecture
3. Modular & Scalable
- Nwidart Laravel Modules for feature isolation
- Service layer for business logic
- Clear separation of concerns
- Ready for horizontal scaling
Tech Stack Decision Making
| Layer | Technology | Why |
|---|---|---|
| Backend | Laravel 12 | Mature ecosystem, excellent security, built-in policies, WebSocket support |
| Frontend | React 19 + Inertia.js | Server-side rendering benefits + React flexibility without API complexity |
| UI Components | Shadcn/UI + TailwindCSS | Accessible, customizable, consistent design system |
| SSH Library | phpseclib3 | Pure PHP, no external SSH dependencies, secure |
| Real-Time | Laravel Reverb | Built into Laravel 12, Pusher alternative, self-hostable |
| Database | PostgreSQL | JSONB support, better for metrics, superior performance |
| Queue | Redis | Fast, reliable, supports retries and prioritization |
Building the VPS Module
I used Laravel Modules (nwidart) to organize features into cohesive packages. The VPS module became the heart of the application.
Database Design: 8 Core Tables
ssh_keys → Encrypted credential storage
servers → VPS instance management
services → Application/service tracking per server
domains → Domain records and DNS
ssl_certs → Certificate lifecycle management
job_logs → Audit trail for async operations
billing_plans → Plan tiers and quotas
server_metrics → CPU, memory, disk, network metrics
Each table was designed with multi-tenant isolation in mind. Every resource except billing_plans has a user_id foreign key.
The SSH Challenge
Managing SSH keys was the trickiest part. Users needed:
- Secure storage (encrypted at rest)
- Support for RSA, ECDSA, and Ed25519 keys
- Quick validation without exposing private keys
- Easy import/export
Solution: Built SSHKeyService with:
// Encryption on store
$encrypted = Crypt::encryptString($privateKey);
// Decryption for operations
$decrypted = Crypt::decryptString($encrypted);
// Fingerprint validation (public key reference)
$fingerprint = hash('sha256', $publicKey);
This let me:
- Store encrypted keys securely
- Validate key format before storing
- Use fingerprints for quick identity checks
- Keep private keys away from logs and outputs
Async SSH Testing with WebSockets
Testing SSH connection directly in a controller? That’s a timeout waiting to happen.
I built TestConnectionJob for background processing:
- User clicks “Test Connection”
- Job is queued with server ID and timeout
- Controller returns immediately with job ID
- Job executes SSH test via phpseclib3
- Result broadcasts via WebSocket to frontend
- Frontend receives update, shows success/failure
This pattern became foundational: queue jobs for long-running operations, broadcast results in real-time.
Real-Time Metrics Dashboard
The dashboard needed to show live metrics without polling. I implemented:
Broadcasting Channel Authorization:
Broadcast::channel('server-metrics.{serverId}', function ($user, $serverId) {
$server = Server::find($serverId);
return $server && $user->can('view', $server);
});
Event Broadcasting:
public function broadcastOn(): Channel
{
return new PrivateChannel("server-metrics.{$this->server->id}");
}
Frontend listens to these channels via Laravel Echo, updating metrics instantly as they arrive.
Frontend: React + Inertia.js
I chose Inertia.js over a pure API approach because:
Pros:
- Less boilerplate than typical REST API + SPA
- Share Laravel data models directly in React
- No session/auth token complexity
- Type-safe route generation
- Server-side pagination/filtering built-in
Pages Implemented:
- Dashboard: Real-time server overview, status indicators, certificate alerts
- Servers: Full CRUD with filtering, bulk operations, status monitoring
- SSH Keys: Key management, import/export, fingerprint validation
- Services: Per-server application monitoring
- Domains: Domain tracking with DNS provider integration
- SSL Certificates: Lifecycle management, expiration alerts
- Billing: Plan selection and usage tracking
Each page used Shadcn/UI components for consistency. The design system gave us free accessibility (Radix UI) and dark mode support.
Key Implementation Challenges
Challenge 1: Multi-Tenant Data Isolation
Problem: A query mishap could leak server data between users.
Solution: Implemented on three levels:
// 1. Model Scope (automatic filtering)
public function scopeVisible(Builder $query)
{
return $query->where('user_id', auth()->id());
}
// 2. Policy Authorization (gate-check)
public function view(User $user, Server $server)
{
return $user->id === $server->user_id;
}
// 3. Channel Authorization (real-time access)
Broadcast::channel('server-metrics.{serverId}', function ($user, $serverId) {
return auth()->user()->can('view', Server::find($serverId));
});
No query escapes without checking user ownership.
Challenge 2: Encryption Key Management
Encrypting SSH keys sounds simple until you realize:
- Need same key for all users (can’t use per-user keys)
- Key rotation is complex
- Secrets can leak in logs
Solution:
- Used Laravel’s built-in
Cryptfacade (APP_KEY) - Keys stay in
.env, never in code - All decryption operations sanitize error messages
- Implemented separate encryption for highly sensitive data
Challenge 3: Real-Time Broadcasting at Scale
Broadcasting to thousands of concurrent users seemed expensive.
Optimization:
- Private channels (only users who own the server receive updates)
- Throttled broadcasts (send metrics every 30s, not every 1s)
- Efficient database queries for metric aggregation
- Redis for fast channel lookup
Challenge 4: SSH Connection Pooling
Thousands of SSH connections would exhaust resources.
Approach:
- phpseclib3 handles connection lifecycle
- Connections created on-demand, destroyed after use
- No persistent pool (tested, not needed)
- Rate limiting prevents connection storms
Development Workflow
I followed a disciplined workflow to keep code quality high:
1. Planning
Before coding, I created detailed plans:
- Component relationships
- API endpoint specifications
- Database schema design
- Security checklist
2. Implementation
- Write service layer first (business logic isolated)
- Build controllers (thin, focused)
- Create React components (consume controllers via Inertia)
- Write tests concurrently
3. Quality Checks
- Type checking (TypeScript frontend, Laravel types)
- Linting (ESLint, Pint)
- Tests for critical paths (auth, multi-tenant isolation)
- Manual testing in browser
4. Git Discipline
Every commit follows conventional commits:
feat: add ssh key fingerprint validation
fix: prevent metric query from leaking other users' data
refactor: extract SSHConnectionService
test: add policy authorization tests
Lessons Learned
1. Laravel Policies are Powerful
Policies became my primary security layer. Every authorization check went through them. This single decision prevented countless security bugs.
2. Async First
The moment I built TestConnectionJob, I started thinking “should this be async?” for any I/O operation. It became the default pattern.
3. WebSockets Beat Polling
Even simple metric polling felt clunky. WebSocket events made the dashboard feel alive. One real-time broadcast beats 100 poll requests.
4. Modular Architecture Scales
The Nwidart module approach let me add features (new controllers, models, migrations) without touching the core. By Phase 2, adding Vultr integration was just a new module.
5. Design Systems Matter
Using Shadcn/UI + TailwindCSS from day one meant I never had inconsistent buttons or missing dark mode. Design systems are force multipliers.
6. Monitoring Matters
The job_logs table tracking every async operation saved hours of debugging. “When did this SSH test run?” was always answerable.
Feature Highlights
Feature 1: One-Click SSH Testing
User clicks "Test" → Job queued → SSH test runs → WebSocket broadcasts result
No manual SSH commands, no connection strings, instant feedback.
Feature 2: Encrypted Key Storage
SSH keys encrypted at rest with AES-256. Fingerprints allow quick validation without exposing private keys.
Feature 3: Real-Time Metrics Dashboard
CPU, memory, disk usage updated live via WebSocket. No refresh needed.
Feature 4: Multi-Tenant Isolation
Each user sees only their servers. Policies enforce this at every layer.
Feature 5: Certificate Expiration Alerts
Dashboard highlights certificates expiring within 30 days. Never miss a renewal again.
Deployment & Operations
Development
composer dev # Runs server, queue worker, Vite, log viewer concurrently
Production Considerations
- Laravel Reverb for WebSocket broadcasting
- Redis for queue and caching
- PostgreSQL for database
- Supervisor/systemd for queue workers
- CloudFlare for CDN and DDoS protection
Monitoring
- Job logs for audit trail
- Server metrics history for trending
- Error logs in storage/logs/
- Health checks on SSH connectivity
Current State & Future
Phase 1 (Complete)
- VPS server management
- SSH key management
- Real-time monitoring
- SSL certificate tracking
- Multi-tenant isolation
- Billing structure
Phase 2 (In Progress)
- Vultr API integration for automated provisioning
- Service detection (Caddy, Fail2ban, WordPress)
- Provider-agnostic deployment
Phase 3+ (Planned)
- Advanced automation and playbooks
- Team collaboration and permissions
- Custom metrics and alerts
- Third-party integrations (monitoring, incident response)
Why This Matters
Kumix.cloud proves you don’t need a team of 10 to build enterprise-grade SaaS.
The right architecture decisions early (multi-tenant first, policies for security, async for I/O, WebSockets for real-time) meant adding features later was straightforward, not refactoring hell.
This is a production-ready platform built with:
- Clean code principles (KISS, DRY, YAGNI)
- Security as a first-class concern
- Real-time user experience
- Modular, scalable architecture
Technical Takeaways
For Backend Engineers:
- Laravel Policies are underutilized but incredibly powerful
- Async + Broadcasting beats polling for UX
- Modular architecture enables rapid feature development
For Frontend Engineers:
- Inertia.js eliminates API/SPA complexity without sacrificing control
- Shadcn/UI + TailwindCSS create consistent, accessible interfaces
- Real-time updates transform UX (WebSockets > polling)
For DevOps/Infrastructure:
- Multi-tenant design baked in early prevents rewrites
- Queue workers handle scale elegantly
- PostgreSQL + Redis combo is battle-tested
For Startup Builders:
- Ship MVP fast with Laravel + React
- Invest in security and multi-tenant isolation early
- One developer can build enterprise SaaS with the right tools
Tools I Used
- Laravel – Backend framework
- React – Frontend framework
- Inertia.js – Server-side React rendering
- Shadcn/UI – Component library
- TailwindCSS – Styling
- phpseclib3 – SSH library
- PostgreSQL – Database
- Redis – Caching and queues
- Laravel Reverb – WebSocket broadcasting
- Vite – Frontend build tool
- TypeScript – Type safety
Conclusion
Building Kumix.cloud taught me that good architecture choices early compound into fast development later.
The decision to:
- Use policies for security (not scattered middleware)
- Broadcast events instead of polling
- Build async-first for I/O operations
- Design modular with nwidart modules
…meant that seven months later, I’m shipping Phase 2 features in days, not weeks.
If you’re building SaaS, the question isn’t “can one person do it?” It’s “are you willing to invest in architecture that scales with your ambitions?”
Kumix.cloud says yes.
What’s Next?
In Phase 2, I’m integrating with cloud provider APIs (Vultr, DigitalOcean) for automated provisioning. The architecture we built handles this with a new module; no core refactoring needed.
That’s the power of getting the foundations right.