Loopar Introduction
loopar-webpage

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

FunctionDescription
Tenant ManagementCreate, configure, start/stop tenants
App DevelopmentBuild and test apps before distribution
Domain AssignmentConfigure local or public domains
Mode ControlSwitch tenants between dev/prod
MonitoringView status of all tenants

Accessing the Dev Site

# After installation
yarn dev

# Access
http://localhost:3000/desk

What You Can Do

  1. Create a new tenant β†’ Tenant Manager β†’ New
  2. Install apps β†’ Select which apps each tenant gets
  3. Assign domain β†’ Local (.localhost) or public
  4. Set mode β†’ Development or Production
  5. 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:

ColumnDescription
NameUnique identifier
PortNetwork port
DomainAssigned domain
Statusonline / stopped / errored
Modedevelopment / production

Creating a Tenant

  1. Click "+ New"
  2. Configure:
FieldRequiredExample
Nameβœ…client-abc
Portβœ…3001
Domain❌client.localhost or client.com
Production❌Toggle on/off
Apps❌Select apps to install
  1. Click "Save"

Tenant Actions

ActionDescription
StartLaunch tenant process
StopGracefully stop tenant
RestartStop + Start
Set on ProductionApply domain + mode + restart
DeleteRemove 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)

.localhost domains resolve to 127.0.0.1 automatically 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:

  1. Calls Caddy's Admin API (localhost:2019)
  2. Registers the domain β†’ tenant port mapping
  3. For public domains: Caddy obtains SSL automatically
  4. 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

FeatureDescription
Individualized NODE_ENVEach tenant can be dev or prod independently
HMR PortDevelopment tenants get Hot Module Replacement
Automatic UpdatesFile 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 yarn CLI 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

FeatureDevelopmentProduction
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

  1. Create tenant in Tenant Manager
  2. Set domain (optional): myapp.localhost
  3. Leave Production off (default)
  4. 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:

  1. Open the tenant
  2. Set Domain: myapp.com
  3. Enable Production Mode: βœ…
  4. 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:

TenantDatabaseUse Case
devSQLiteQuick development
client-aMySQLProduction, high traffic
client-bPostgreSQLComplex queries
demoSQLiteLightweight 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

AspectBehavior
CodeShared (from /apps)
DataIsolated per tenant
ConfigIndependent per tenant
UsersSeparate 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

ResourceMinimumRecommended
CPU1 core2+ cores
RAM1 GB2+ GB
Storage10 GB SSD20+ GB SSD
Node.js22.xLatest LTS
OSUbuntu 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

TenantsRAMCPU
1-52 GB2 cores
5-204 GB4 cores
20-508 GB8 cores
50+16+ GB16+ 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
  • DigitalOcean: Great for small-medium
  • Vultr: Good price/performance
  • Hetzner: Excellent value in Europe
  • AWS/GCP: Enterprise scale