Tenanto
Production-ready Laravel Multi-Tenant SaaS Boilerplate
A comprehensive Laravel 13 boilerplate for building multi-tenant SaaS applications with built-in billing, team management, and admin panels.
📖 New to Tenanto? Start with the Buyer User Guide for a step-by-step installation walkthrough, panel tours with screenshots, and a troubleshooting playbook.
Features
Multi-Tenancy
- Single Database Architecture - Efficient tenant isolation via
tenant_idcolumn - Subdomain & Custom Domain - Flexible tenant identification
- Automatic Scoping - Global scopes ensure data isolation
- Defense-in-Depth - Policy layer double-checks tenant ownership
Authentication & Authorization
- Laravel Breeze + Livewire - Modern authentication flows
- Email Verification - Required before access
- Role-Based Access Control - Owner, Admin, Member roles per tenant
- spatie/laravel-permission - Granular permission management
Billing & Subscriptions
- Stripe Integration - Via Laravel Cashier
- Subscription Plans - Basic, Pro, Enterprise tiers
- Feature Limits - Users, teams, projects per plan
- Trial Support - Configurable trial periods
- Webhook Handling - Automatic subscription lifecycle management
Admin Panels (FilamentPHP 5)
- System Admin (
/admin) - Tenant management, licenses, system users - Tenant Admin (
/app) - Complete per-tenant management:- Users - Create users, send invitations, manage roles (Owner/Admin/Member)
- Invitations - Track pending, accepted, expired, and revoked invitations
- Teams - Create teams, manage members
- Projects - Example CRUD module with tasks
- Billing - Subscription management, invoices
- Beautiful UI - Modern, responsive design
API
- Laravel Sanctum - Token-based authentication
- RESTful Design - Version-prefixed endpoints (
/api/v1/) - Rate Limiting - Tiered limits (30/60/120 req/min)
- Full CRUD - Projects, Tasks example implementation
Marketplace & Distribution
- Demo Automation - Auto-provisioned demo tenants with scheduled cleanup
- License Management - HMAC-validated license keys with tier-based features
- Update Notifications - Automatic new version detection
- Onboarding Wizard - Guided setup for new customers
- Support System - Database-backed support ticket infrastructure with provider abstraction
Internationalization (i18n)
- Translation Ready - Complete i18n infrastructure prepared
- Per-Tenant Locale - Each tenant can have their own language
- Automatic Detection - Browser Accept-Language header support
- 11 Translation Files - All application strings externalized
Developer Experience
- PHP 8.4 - Modern PHP features
- PHPStan Level 8 - Maximum type safety (0 errors)
- Laravel Pint - Consistent code style
- Automated Tests - 600+ backend test methods + 480+ Playwright E2E scenarios
- Docker Ready - One-command development setup (8 services)
- Laravel Horizon - Queue dashboard with job metrics and monitoring
Quality & CI
- GitLab CI Included - Build, lint, test, security checks, and demo deployment from the default branch
- Automated QA - PHPUnit, Pint, PHPStan, Playwright documented in
docs/e2e-testing.md
Requirements
- PHP 8.3+ (8.4 recommended)
- PostgreSQL 16+
- Redis 7+
- Node.js 20+
- Docker Desktop (recommended)
- PHP extensions: pdo_pgsql, redis, zip
Quick Start
1. Clone the Repository
# Replace with your repository URL
git clone https://your-git-host.com/your-org/tenanto.git
cd tenanto
2. Start Docker Environment
docker compose up -d
3. Install Dependencies
docker compose exec app composer install
Frontend assets: No
npm installon the host. TheviteDocker service runsnpm ciautomatically on first start and serves assets with hot module replacement athttp://localhost:5273. Blade/CSS/JS edits reload instantly. A production build (npm run build) is only required when deploying without the Vite service.
4. Configure Environment
cp .env.example .env
docker compose exec app php artisan key:generate
Edit .env with your settings:
- Database credentials
- Stripe API keys
- Mail configuration
LEGAL_*operator details for the bundled Terms of Service and Privacy Policy templates (see the launch checklist for the full list)
5. Run Migrations & Seed
docker compose exec app php artisan migrate --seed
6. Configure Local DNS
Add to your hosts file:
- Linux/macOS:
/etc/hosts - Windows:
C:\Windows\System32\drivers\etc\hosts
127.0.0.1 tenanto.local
127.0.0.1 admin.tenanto.local
127.0.0.1 demo.tenanto.local
7. Access the Application
| URL | Redirects To | Credentials |
|---|---|---|
| http://tenanto.local | Landing page | - |
| http://admin.tenanto.local | → /admin (System Admin) |
[email protected] / ChangeMe-DemoPass! |
| http://demo.tenanto.local | → /app (Tenant App) |
[email protected] / ChangeMe-DemoPass! |
| http://demo.tenanto.local/api/v1 | API | Bearer token |
| http://admin.tenanto.local/horizon | Queue Dashboard | System admin only |
| http://localhost:8025 | Email Testing | No auth required |
Default credentials use the
DEMO_DEFAULT_PASSWORDvalue from.env.example(ChangeMe-DemoPass!). Edit.envand re-runphp artisan migrate --seedto seed with a different password, or runphp artisan demo:reset-passwordsafter editing.envto rotate existing accounts in place. Always change the default before going public.
URL Structure:
- Main domain (
tenanto.local) - Marketing/landing page - Admin subdomain (
admin.tenanto.local) - Auto-redirects to Filament admin panel - Tenant subdomains (
{tenant}.tenanto.local) - Auto-redirects to Filament tenant app
Docker Services
The docker compose up -d command starts all 8 services automatically:
| Service | Container | Port | Description |
|---|---|---|---|
| app | tenanto_app | - | PHP-FPM 8.4 application server |
| nginx | tenanto_nginx | 80, 443 | Web server with SSL support |
| db | tenanto_db | 5432 | PostgreSQL 16 database |
| redis | tenanto_redis | 6379 | Cache, sessions, and queue broker |
| queue | tenanto_queue | - | Laravel Horizon (background jobs) |
| scheduler | tenanto_scheduler | - | Laravel task scheduler (cron jobs) |
| mailhog | tenanto_mailhog | 1025, 8025 | SMTP server + Email testing UI |
| vite | tenanto_vite | 5273 | Node 24 Vite dev server with hot module replacement (dev only) |
Service Details
- Queue Worker - Processes background jobs: emails, Stripe webhooks, notifications
- Scheduler - Runs cron jobs: demo tenant cleanup, subscription reminders
- Mailhog - Catches all outgoing emails at http://localhost:8025
- Vite - Node 24 dev server. Host port
5273→ container port5173(override viaVITE_HOST_PORTin.envif 5273 is already in use). Not included indocker-compose.prod.yml; production serves assets frompublic/build/afternpm run build.
Useful Commands
# View all container logs
docker compose logs -f
# View specific service logs
docker compose logs -f queue
# Restart all services
docker compose restart
# Rebuild containers (after Dockerfile changes)
docker compose up -d --build
# Stop all services
docker compose down
Project Structure
app/
├── Domain/ # Business logic (DDD-lite)
│ ├── Authorization/ # Roles & permissions
│ ├── Billing/ # Stripe/subscription logic
│ ├── Demo/ # Demo tenant automation
│ ├── ExampleApp/ # Demo module (Projects/Tasks)
│ ├── Licensing/ # License key management
│ ├── Onboarding/ # Customer onboarding wizard
│ ├── Support/ # Support ticket system
│ ├── Tenancy/ # Multi-tenant core
│ └── Updates/ # Version update notifications
├── Filament/
│ ├── Admin/ # System admin panel
│ └── Tenant/ # Per-tenant admin panel
└── Http/
├── Controllers/Api/ # API controllers
└── Middleware/ # Tenant middleware
docs/
├── api.md # API documentation
├── authorization.md # Roles & permissions guide
├── backup-restore.md # Backup & recovery guide
├── buyer-guide.md # Marketplace buyer guide (FAQ, licensing)
├── deployment.md # Deployment guide
├── e2e-testing.md # Playwright test suite documentation
├── example-module.md # Module development guide
├── filament.md # FilamentPHP customization
├── i18n.md # Internationalization guide
├── multi-tenancy.md # Multi-tenancy architecture
├── performance.md # Performance optimization
├── security.md # Security checklist
├── troubleshooting.md # Common issues and solutions
└── user-guide/ # Buyer-focused walkthroughs with screenshots
├── installation.md
├── admin-panel.md
├── tenant-panel.md
├── billing.md
├── api-quickstart.md
├── customization.md
├── support-policy.md
├── troubleshooting.md
└── images/ # Screenshots of admin + tenant panels
tests/
├── Feature/ # Integration tests
└── Unit/ # Unit tests
e2e/ # Playwright E2E tests
├── admin/ # Admin panel tests
├── auth/ # Authentication tests
├── api/ # API tests
├── errors/ # Error page tests
├── helpers/ # Test utilities (auth, api)
├── isolation/ # Tenant isolation tests
├── navigation/ # Navigation tests
├── onboarding/ # Onboarding wizard tests
└── tenant/ # Tenant panel tests
Getting Started for Developers
This section guides you through customizing Tenanto for your own SaaS product.
Step 1: Remove the Example Module
The ExampleApp module (Projects/Tasks) is included as a reference implementation. To remove it:
# Remove domain files
rm -rf app/Domain/ExampleApp
# Remove Filament resources
rm -rf app/Filament/Tenant/Resources/ProjectResource
rm -rf app/Filament/Tenant/Resources/TaskResource
rm -rf app/Filament/Tenant/Resources/ProjectResource.php
rm -rf app/Filament/Tenant/Resources/TaskResource.php
rm -rf app/Filament/Tenant/Widgets/ProjectStatsWidget.php
rm -rf app/Filament/Tenant/Widgets/TaskStatsWidget.php
# Remove API controllers
rm app/Http/Controllers/Api/V1/ProjectController.php
rm app/Http/Controllers/Api/V1/TaskController.php
# Remove migrations
rm database/migrations/*_create_projects_table.php
rm database/migrations/*_create_tasks_table.php
# Remove factories, seeders, and tests
rm database/factories/ProjectFactory.php
rm database/factories/TaskFactory.php
rm database/seeders/ExampleAppSeeder.php
rm -rf tests/Feature/Api/Project*
rm -rf tests/Feature/Api/Task*
# Clean up API routes
# Edit routes/api.php and remove Project/Task routes
Step 2: Create Your Own Module
Follow this pattern to add tenant-scoped models:
// 1. Create Model with BelongsToTenant trait
namespace App\Domain\YourModule\Models;
use App\Domain\Tenancy\Traits\BelongsToTenant;
use Illuminate\Database\Eloquent\Model;
class YourModel extends Model
{
use BelongsToTenant;
protected $fillable = ['tenant_id', 'name', ...];
}
// 2. Create Migration with tenant_id
Schema::create('your_models', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
$table->string('name');
$table->timestamps();
// Always add composite index for tenant queries
$table->index(['tenant_id', 'created_at']);
});
// 3. Create Policy for authorization
namespace App\Policies;
use App\Domain\YourModule\Models\YourModel;
use App\Models\User;
class YourModelPolicy
{
public function viewAny(User $user): bool
{
return $user->can('view your-models');
}
public function view(User $user, YourModel $model): bool
{
// Defense-in-depth: verify tenant ownership
return $user->tenant_id === $model->tenant_id;
}
}
// 4. Create Filament Resource
// See docs/example-module.md for complete guide
Step 3: Configure Your Plans
Edit config/billing.php to define your subscription tiers:
'plans' => [
'starter' => [
'name' => 'Starter',
'stripe_price_id' => env('STRIPE_PRICE_STARTER'),
'price_monthly' => (int) env('BILLING_PRICE_STARTER_MONTHLY', 29),
'features' => [
'users' => 3,
'your_feature' => 100,
'premium_feature' => false,
],
],
// Add more plans...
],
Step 4: Customize Branding
- Application Name: Edit
APP_NAMEin.env - Colors: Edit
app/Filament/Tenant/TenantPanelProvider.php - Logo: Add to
public/images/and update panel config - Email Templates: Edit
resources/views/mail/
Step 5: Set Up Stripe
- Create products/prices in Stripe Dashboard
- Copy Price IDs to
.env:STRIPE_PRICE_STARTER=price_xxx STRIPE_PRICE_PRO=price_yyy BILLING_PRICE_STARTER_MONTHLY=29 BILLING_PRICE_PRO_MONTHLY=79 - Set up webhooks pointing to
/stripe/webhook - Test with Stripe CLI:
stripe listen --forward-to localhost/stripe/webhook
Key Concepts
| Concept | Location | Purpose |
|---|---|---|
BelongsToTenant |
app/Domain/Tenancy/Traits/ |
Auto-scopes models to current tenant |
TenantScope |
app/Domain/Tenancy/Scopes/ |
Filters all queries by tenant_id |
TenantMiddleware |
app/Http/Middleware/ |
Resolves tenant from subdomain/domain |
PlanFeatureService |
app/Domain/Billing/Services/ |
Checks feature limits per plan |
For detailed documentation, see docs/example-module.md.
Development Commands
The most common workflows, all run through docker compose exec app:
# Start development environment
docker compose up -d
# Run all PHP tests
docker compose exec app php artisan test
# Static analysis (PHPStan level 8)
docker compose exec app ./vendor/bin/phpstan analyse
# Code style fixing (Laravel Pint)
docker compose exec app ./vendor/bin/pint
# Fresh database with seeds
docker compose exec app php artisan migrate:fresh --seed
# Open a shell in the app container
docker compose exec app bash
API Overview
Base URL: /api/v1/
Authentication
POST /api/v1/auth/login
POST /api/v1/auth/logout
GET /api/v1/auth/me
Resources
# Projects
GET /api/v1/projects
POST /api/v1/projects
GET /api/v1/projects/{id}
PUT /api/v1/projects/{id}
DELETE /api/v1/projects/{id}
POST /api/v1/projects/{id}/archive
POST /api/v1/projects/{id}/unarchive
# Tasks
GET /api/v1/projects/{id}/tasks
POST /api/v1/projects/{id}/tasks
GET /api/v1/tasks/{id}
PUT /api/v1/tasks/{id}
DELETE /api/v1/tasks/{id}
POST /api/v1/tasks/{id}/complete
See docs/api.md for complete API documentation.
Testing
PHP Tests (PHPUnit)
# Run all PHP tests
docker compose exec app php artisan test
# Run with coverage
docker compose exec app php artisan test --coverage
# Run specific group
docker compose exec app php artisan test --group=tenant-isolation
Test Groups:
tenant-isolation- Critical security testsapi- API endpoint testsbilling- Subscription/payment tests
E2E Tests (Playwright)
# Install Playwright
npm install
npx playwright install chromium
# Run all E2E tests
npx playwright test --project=chromium
# Run specific test file
npx playwright test e2e/auth/login.spec.ts
# Run with UI mode
npx playwright test --ui
# View HTML report
npx playwright show-report
E2E Test Coverage:
| Category | Description |
|---|---|
| Auth | Login, logout, register, password reset, email verification |
| Admin Panel | Tenants, users, licenses, dashboard |
| Tenant Panel | Projects, tasks, teams, users, profile, billing |
| API | Authentication, CRUD operations |
| Isolation | Tenant isolation, permission enforcement |
| Navigation | Route testing, menu visibility |
| Onboarding | Wizard flow |
| Errors | Error pages (403, 404, 500) |
Configuration: playwright.config.ts
Configuration
Environment Variables
Key variables in .env:
# Tenancy
TENANCY_MODE=subdomain
TENANCY_CENTRAL_DOMAINS=tenanto.local,admin.tenanto.local
# Billing
STRIPE_KEY=pk_test_xxx
STRIPE_SECRET=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
# Plans
STRIPE_PRICE_BASIC=price_xxx
STRIPE_PRICE_PRO=price_xxx
STRIPE_PRICE_ENTERPRISE=price_xxx
BILLING_PRICE_BASIC_MONTHLY=29
BILLING_PRICE_PRO_MONTHLY=79
BILLING_PRICE_ENTERPRISE_MONTHLY=199
# Legal / Compliance (drives the /terms and /privacy pages)
LEGAL_OPERATOR_NAME="Acme SaaS s.r.o."
LEGAL_OPERATOR_ADDRESS="Main Street 1, 110 00 Prague, Czech Republic"
LEGAL_JURISDICTION="Czech Republic"
LEGAL_GOVERNING_LAW="Czech Republic"
LEGAL_VENUE="Czech Republic"
[email protected]
[email protected]
LEGAL_SUPERVISORY_AUTHORITY="the Office for Personal Data Protection (ÚOOÚ)"
See .env.example for all options with documentation.
The bundled Terms of Service and Privacy Policy pages show a "Template — customize before publishing" banner until the
LEGAL_*variables are filled in, so you cannot accidentally expose unconfigured legal text to real users. Have the final rendered pages reviewed by qualified counsel in your jurisdiction before go-live — the templates are a solid starting point, not legal advice.
Subscription Plans
Configure in config/billing.php:
'plans' => [
'basic' => [
'name' => 'Basic',
'stripe_price_id' => env('STRIPE_PRICE_BASIC'),
'price_monthly' => (int) env('BILLING_PRICE_BASIC_MONTHLY', 29),
'features' => [
'users' => 5,
'teams' => 1,
'projects' => 10,
'storage_gb' => 5,
'api_requests_per_day' => 1000,
'custom_domain' => false,
'priority_support' => false,
],
],
// ...
],
Deployment
See docs/deployment.md for production deployment instructions.
Quick Checklist
- Set
APP_ENV=production - Set
APP_DEBUG=false - Configure production database
- Set up Stripe production keys
- Configure mail provider
- Set up SSL certificates (including a wildcard certificate for tenant subdomains)
- Configure queue workers and scheduler (Supervisor + cron)
- Set up monitoring (Sentry, log aggregation)
- Run
php artisan optimize+php artisan migrate --forceon each release
Security
Built-in Protections
- Tenant Isolation - Global scopes + policy checks
- CSRF Protection - All forms protected
- Rate Limiting - API and auth endpoints
- XSS Prevention - Blade auto-escaping
- SQL Injection - Eloquent prepared statements
- Secure Headers - CSP, X-Frame-Options, etc.
See docs/security.md for security checklist.
Documentation
| Document | Description |
|---|---|
| User Guide | Buyer-focused walkthroughs (installation, panels, billing, customization) |
| Marketplace Buyer Guide | Marketplace-focused setup, FAQ, licensing, refund guidance |
| Multi-Tenancy Architecture | Tenant isolation and scoping |
| Authorization & Permissions | RBAC and policies |
| FilamentPHP Customization | Admin panel configuration |
| API Documentation | Complete API reference |
| Deployment Guide | Production deployment |
| Security Checklist | Security best practices |
| Example Module | Building custom modules |
| Performance Guide | Optimization strategies |
| i18n Guide | Internationalization setup |
| Backup & Restore | Backup strategies and recovery |
| E2E Testing Guide | Playwright test suite documentation |
| Troubleshooting | Common issues and solutions |
| UPGRADING | Version upgrade instructions |
| CHANGELOG | Version history and changes |
| Third-Party Licenses | Dependency licenses |
Tech Stack
| Component | Technology |
|---|---|
| Framework | Laravel 13 |
| PHP Version | 8.3+ (8.4 recommended) |
| Admin Panel | FilamentPHP 5 |
| Frontend | Livewire 4 + Tailwind CSS 4 |
| Database | PostgreSQL 16 |
| Cache/Queue | Redis 7 |
| API Auth | Laravel Sanctum |
| Billing | Laravel Cashier (Stripe) |
| Permissions | spatie/laravel-permission |
| Testing | PHPUnit (600+ backend test methods) |
| E2E Testing | Playwright (480+ scenarios across chromium, firefox, webkit + mobile viewports) |
| Static Analysis | PHPStan Level 8 |
| Code Style | Laravel Pint |
Support
Support Contact: Configure SUPPORT_EMAIL in .env before publishing buyer-facing support instructions.
Response Time:
- General questions: 24-48 hours
- Bug reports: 24-48 hours (critical bugs prioritized)
- Feature requests: Reviewed weekly
What's Included:
- Installation assistance
- Bug fixes
- Documentation clarification
- Configuration questions
What's Not Included:
- Custom feature development
- Third-party integration setup
- Server/hosting configuration
- Code customization beyond documentation
Support is provided according to the support terms and marketplace policy you publish with the product listing.
See docs/buyer-guide.md for marketplace-focused buyer information. See docs/troubleshooting.md for common issues and solutions.
License
Tenanto is commercial software. See LICENSE for details.
Third-party dependencies are licensed under MIT, BSD-3-Clause, and Apache-2.0 licenses. See THIRD_PARTY_LICENSES.md for details.
Credits
Built with: