Architecture Overview
Loopar is built on a multi-tenant architecture from the ground up. This isn't just a production featureβit's the core of how Loopar works.
The Dev Site
When you install Loopar, you get a dev site. This is your administration center:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DEV SITE (Admin) β
β localhost:3000 β
β β
β β’ Create and manage all tenants β
β β’ Develop and test apps β
β β’ Control dev/prod mode per tenant β
β β’ Always runs in development mode β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Tenants
From the dev site, you create tenants. Each tenant:
- Runs as an independent PM2 process
- Has its own database (isolated data)
- Can be in development or production mode
- Can have a domain (local or public)
DEV SITE (localhost:3000)
β
ββββΊ Tenant A (:3001) β development β client.localhost
ββββΊ Tenant B (:3002) β production β client.com
ββββΊ Tenant C (:3003) β development β demo.localhost
ββββΊ Tenant D (:3004) β production β app.company.com
Key Concept: Dev/Prod is configured per tenant, not globally. You can have development and production tenants running simultaneously on the same server.
The Dev Site
The dev site is the control center for your entire Loopar installation.
Responsibilities
| Function | Description |
|---|---|
| Tenant Management | Create, configure, start/stop tenants |
| App Development | Build and test apps before distribution |
| Domain Assignment | Configure local or public domains |
| Mode Control | Switch tenants between dev/prod |
| Monitoring | View status of all tenants |
Accessing the Dev Site
# After installation
yarn dev
# Access
http://localhost:3000/desk
What You Can Do
- Create a new tenant β Tenant Manager β New
- Install apps β Select which apps each tenant gets
- Assign domain β Local (.localhost) or public
- Set mode β Development or Production
- Deploy β Click "Set on Production"
Dev Site Security
The dev site should never be publicly accessible:
β
localhost:3000 β Private, admin only
β
admin.localhost:3000 β Private with Caddy
β admin.mycompany.com β Don't expose publicly
Tip: Keep the dev site on localhost or behind a VPN. All tenant management happens here.
Tenant Manager
The Tenant Manager is where you control all your tenants.
Accessing
Dev Site β Desk β Setup β Tenant Manager
Tenant List
Shows all tenants with their current status:
| Column | Description |
|---|---|
| Name | Unique identifier |
| Port | Network port |
| Domain | Assigned domain |
| Status | online / stopped / errored |
| Mode | development / production |
Creating a Tenant
- Click "+ New"
- Configure:
| Field | Required | Example |
|---|---|---|
| Name | β | client-abc |
| Port | β | 3001 |
| Domain | β | client.localhost or client.com |
| Production | β | Toggle on/off |
| Apps | β | Select apps to install |
- Click "Save"
Tenant Actions
| Action | Description |
|---|---|
| Start | Launch tenant process |
| Stop | Gracefully stop tenant |
| Restart | Stop + Start |
| Set on Production | Apply domain + mode + restart |
| Delete | Remove tenant completely |
Status Indicators
π’ Online β Running normally
π΄ Stopped β Not running
π‘ Starting β In progress
π΅ Errored β Check logs with: pm2 logs tenant-name
Caddy Reverse Proxy
Caddy handles domain routing for both development and production.
How It Works
CADDY
β
βββββββββββββββββββΌββββββββββββββββββ
β β β
βΌ βΌ βΌ
client.localhost demo.localhost client.com
β β β
βΌ βΌ βΌ
:3001 :3002 :3003
(tenant A) (tenant B) (tenant C)
Development Domains (.localhost)
For local development, use .localhost domains:
client.localhost β Tenant A (port 3001)
demo.localhost β Tenant B (port 3002)
test.localhost β Tenant C (port 3003)
.localhostdomains resolve to127.0.0.1automatically in most browsers.
Production Domains
For public access, use real domains:
client.com β Tenant A (port 3001) + Auto SSL
app.company.com β Tenant B (port 3002) + Auto SSL
Installation
Ubuntu/Debian
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
macOS
brew install caddy
Start Caddy
caddy start
Automatic Registration
When you click "Set on Production" in Tenant Manager, Loopar:
- Calls Caddy's Admin API (
localhost:2019) - Registers the domain β tenant port mapping
- For public domains: Caddy obtains SSL automatically
- Traffic flows through Caddy to the tenant
You don't need to configure Caddy manuallyβLoopar handles it.
PM2 Process Manager
PM2 manages all tenant processes. Each tenant runs as an independent Node.js process.
Ecosystem Configuration
Loopar automatically generates loopar.ecosystem.config.mjs:
export default {
apps: [
{
name: 'dev',
script: 'node_modules/loopar/bin/pm2-wrapper.js',
env: {
NODE_ENV: 'development',
TENANT_ID: 'dev',
PORT: 3000,
HMR_PORT: 13000
}
},
{
name: 'client-abc',
script: 'node_modules/loopar/bin/pm2-wrapper.js',
env: {
NODE_ENV: 'production', // β Per tenant!
TENANT_ID: 'client-abc',
PORT: 3001,
DOMAIN: 'client.com'
}
},
{
name: 'demo',
script: 'node_modules/loopar/bin/pm2-wrapper.js',
env: {
NODE_ENV: 'development', // β Per tenant!
TENANT_ID: 'demo',
PORT: 3002,
DOMAIN: 'demo.localhost',
HMR_PORT: 13002
}
}
]
};
Key Points
| Feature | Description |
|---|---|
| Individualized NODE_ENV | Each tenant can be dev or prod independently |
| HMR Port | Development tenants get Hot Module Replacement |
| Automatic Updates | File regenerates when tenants change |
Useful Commands
Loopar's CLI wraps PM2 β prefer these from the project root:
# View all tenants in this Loopar daemon
yarn list
# Tail logs for all tenants, or one
yarn logs
yarn logs client-abc
# Restart / stop / start a tenant
yarn restart client-abc
yarn stop client-abc
yarn start client-abc
Raw PM2 commands still work if you need them (pm2 monit, pm2 env client-abc).
Note: While you can use PM2 commands directly, the
yarnCLI and the Desk interface target the correct project-local daemon automatically.
Development Mode
Tenants in development mode have features optimized for building and testing.
Features
| Feature | Development | Production |
|---|---|---|
| Hot Module Replacement | β Enabled | β Disabled |
| Source Maps | β Full | β Minimal |
| Error Details | β Verbose | β Generic |
| Asset Caching | β Disabled | β Aggressive |
| Code Minification | β No | β Yes |
Setting Up Development Tenant
- Create tenant in Tenant Manager
- Set domain (optional):
myapp.localhost - Leave Production off (default)
- Save
Workflow
Dev Site (localhost:3000)
β
β Create tenant "myapp" with:
β - Port: 3001
β - Domain: myapp.localhost
β - Production: OFF
β
βΌ
myapp.localhost βββΊ Caddy βββΊ :3001 (development mode)
β
βββ HMR enabled
βββ Fast refresh
βββ Detailed errors
Environment Variables (Development)
NODE_ENV=development
TENANT_ID=myapp
PORT=3001
HMR_PORT=13001
DOMAIN=myapp.localhost
Production Mode
Tenants in production mode are optimized for performance and security.
Features
- β Minified and bundled assets
- β Aggressive caching
- β Automatic SSL (via Caddy)
- β Generic error messages
- β Optimized database queries
- β No HMR or watch mode
Deploying to Production
Step 1: DNS Setup (for public domains)
Point your domain to your server:
A Record: myapp.com β YOUR_SERVER_IP
Step 2: Configure Tenant
In Tenant Manager:
- Open the tenant
- Set Domain:
myapp.com - Enable Production Mode: β
- Click "Set on Production"
Step 3: What Happens
"Set on Production" clicked
β
ββββΊ Update ecosystem: NODE_ENV=production
ββββΊ Register domain with Caddy
ββββΊ Caddy obtains SSL certificate (public domains)
ββββΊ Restart tenant with new config
β
βΌ
https://myapp.com is live! π
Environment Variables (Production)
NODE_ENV=production
TENANT_ID=myapp
PORT=3001
DOMAIN=myapp.com
IS_PRODUCTION=true
Verify Deployment
# Check PM2
pm2 list
# Should show: myapp | online | prod
# Check Caddy routes
curl http://localhost:2019/config/apps/http/servers/srv0/routes
# Test domain
curl -I https://myapp.com
Database Isolation
Each tenant has a completely separate database. There is no shared data.
Database Options
Each tenant can use a different database type:
| Tenant | Database | Use Case |
|---|---|---|
| dev | SQLite | Quick development |
| client-a | MySQL | Production, high traffic |
| client-b | PostgreSQL | Complex queries |
| demo | SQLite | Lightweight demos |
Configuration
Each tenant has its own .env file:
# /sites/client-abc/.env
# SQLite (simplest)
DB_TYPE=sqlite
# MySQL
DB_TYPE=mysql
DB_HOST=localhost
DB_PORT=3306
DB_NAME=client_abc_db
DB_USER=client_abc
DB_PASSWORD=secure_password
# PostgreSQL
DB_TYPE=postgres
DB_HOST=localhost
DB_PORT=5432
DB_NAME=client_abc_db
DB_USER=client_abc
DB_PASSWORD=secure_password
Creating MySQL Database for Tenant
CREATE DATABASE client_abc_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
CREATE USER 'client_abc'@'localhost'
IDENTIFIED BY 'secure_password';
GRANT ALL PRIVILEGES ON client_abc_db.*
TO 'client_abc'@'localhost';
FLUSH PRIVILEGES;
Isolation Guarantees
- β Tenants cannot access each other's data
- β Credentials are isolated per tenant
- β Migrations run independently
- β Backup/restore per tenant
App Distribution
Apps are developed in the dev site and distributed to tenants.
How It Works
Dev Site
β
β Develop apps: CRM, Blog, ERP
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β Tenant A β Tenant B β Tenant C β
β Apps: β Apps: β Apps: β
β - CRM β - Blog β - CRM β
β - ERP β - Shop β β
βββββββββββββββββββββββββββββββββββββββ
Installing Apps
On Tenant Creation
New Tenant β Apps β [x] CRM, [x] Blog, [ ] ERP β Save
Post-Creation
Tenant β Apps β Install β Select App β Install
App Isolation
| Aspect | Behavior |
|---|---|
| Code | Shared (from /apps) |
| Data | Isolated per tenant |
| Config | Independent per tenant |
| Users | Separate per tenant |
Common Patterns
SaaS Model
Same app, different customers:
CRM App (v1.0)
β
ββββΊ Company A (50 users, MySQL)
ββββΊ Company B (200 users, PostgreSQL)
ββββΊ Company C (10 users, SQLite)
Agency Model
Different apps per client:
ββββΊ Client A: E-commerce + Blog
ββββΊ Client B: CRM + Dashboard
ββββΊ Client C: Custom App
Server Requirements
Minimum Requirements
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 1 core | 2+ cores |
| RAM | 1 GB | 2+ GB |
| Storage | 10 GB SSD | 20+ GB SSD |
| Node.js | 22.x | Latest LTS |
| OS | Ubuntu 20.04+ | Ubuntu 22.04+ |
Per-Tenant Resources
Each tenant consumes approximately:
- Memory: 100-200 MB idle, 300-500 MB active
- CPU: Minimal idle, scales with traffic
- Disk: Depends on uploads and data
Scaling Guidelines
| Tenants | RAM | CPU |
|---|---|---|
| 1-5 | 2 GB | 2 cores |
| 5-20 | 4 GB | 4 cores |
| 20-50 | 8 GB | 8 cores |
| 50+ | 16+ GB | 16+ cores |
Firewall (Production)
# UFW (Ubuntu)
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
# Block direct tenant port access
sudo ufw deny 3000:9999/tcp
Recommended Providers
- DigitalOcean: Great for small-medium
- Vultr: Good price/performance
- Hetzner: Excellent value in Europe
- AWS/GCP: Enterprise scale