[Architecture] Multi-tenant provisioning system — VPS-based, single codebase, per-tenant DB + domain routing #185

Open
opened 2026-06-06 11:39:45 +00:00 by jmiller · 2 comments
Owner

Goal

Run MokoWaaS as multi-tenant Joomla 6: one shared codebase to maintain, many client sites, automated client spin-up.

Decision summary

  • Single shared Joomla 6 codebase. Multi-tenancy via per-tenant database + domain-based routing — NOT row-level (no tenant_id in core tables) and NOT per-tenant symlinked directories (static assets 404 without symlinks).
  • Tenant resolved by HTTP_HOST at bootstrap: dynamic configuration.php (JConfig __construct reads a tenants map) + custom defines.php / administrator/defines.php for per-tenant JPATH_CACHE, tmp, log, and images paths.
  • One document root; every tenant domain points at it.
  • Two domains per client: dev. (always online for build-out) and production (toggleable online/offline).
  • Online/offline handled by a domain-aware "soft offline" flag in the bootstrap layer — NOT Joomla's native offline mode, which is global per database and can't keep dev up while prod is down.
  • SSL: on-demand TLS per hostname (Caddy/Traefik) or wildcard via certbot DNS-01 against the DreamHost DNS API. dev subdomains use CNAME; apex domains need an A record (CNAME illegal at apex).

Why VPS (do not revisit shared hosting)

  • DreamHost API removed the database, domain, mail, and user command groups — only DNS commands remain.
  • DreamHost shared MySQL cannot run CREATE DATABASE (panel-only), so DB creation can't be scripted there.
  • DreamHost shared has no wildcard SSL; its managed Let's Encrypt only covers fully-hosted domains (HTTP-01, no DNS-01).
  • A VPS restores: own web server (on-demand/wildcard TLS), own MySQL (scriptable CREATE DATABASE -> real per-client DB isolation), and true one-script spin-up.

Scope / tasks

  • Provision VPS (evaluate DreamCompute vs Hetzner vs DigitalOcean): Ubuntu, MySQL 8, PHP, web server
  • Reverse proxy with on-demand TLS (Caddy or Traefik) + authorization endpoint gating allowed hostnames
  • Single shared Joomla 6 install; finalize document-root strategy
  • Dynamic configuration.php resolving tenant by HTTP_HOST
  • Custom defines.php + administrator/defines.php for per-tenant cache/tmp/log/images
  • Tenant registry (flat tenants.php vs control-plane DB vs small management component): host -> {db creds, paths, secret, role (dev/prod), offline flag}
  • Domain-aware soft-offline: dev host always serves; prod host short-circuits to a "coming soon" page when flagged
  • Provisioning script: create DB + user, seed Joomla schema, write tenant entry, create writable dirs, fire DNS record via DreamHost DNS API
  • Per-tenant media isolation (images path per tenant)
  • Per-tenant DB schema migration runner (file updates once, DB migrations N times)
  • Per-tenant backup/restore (Borg) + monitoring hooks
  • De-provisioning / offboarding flow

Open questions

  • DB-per-client vs single-DB-prefix on the VPS (leaning per-client now that DB creation is scriptable)
  • dev -> prod promotion: same DB with a flag, or separate dev/prod DBs with a content clone at launch?
  • Tenant registry: flat PHP file vs control-plane database vs management component
  • Client-owned domains vs platform subdomains (affects SSL: on-demand per-domain vs single wildcard)
## Goal Run MokoWaaS as multi-tenant Joomla 6: one shared codebase to maintain, many client sites, automated client spin-up. ## Decision summary - Single shared Joomla 6 codebase. Multi-tenancy via per-tenant database + domain-based routing — NOT row-level (no tenant_id in core tables) and NOT per-tenant symlinked directories (static assets 404 without symlinks). - Tenant resolved by HTTP_HOST at bootstrap: dynamic configuration.php (JConfig __construct reads a tenants map) + custom defines.php / administrator/defines.php for per-tenant JPATH_CACHE, tmp, log, and images paths. - One document root; every tenant domain points at it. - Two domains per client: dev.<client> (always online for build-out) and <client> production (toggleable online/offline). - Online/offline handled by a domain-aware "soft offline" flag in the bootstrap layer — NOT Joomla's native offline mode, which is global per database and can't keep dev up while prod is down. - SSL: on-demand TLS per hostname (Caddy/Traefik) or wildcard via certbot DNS-01 against the DreamHost DNS API. dev subdomains use CNAME; apex domains need an A record (CNAME illegal at apex). ## Why VPS (do not revisit shared hosting) - DreamHost API removed the database, domain, mail, and user command groups — only DNS commands remain. - DreamHost shared MySQL cannot run CREATE DATABASE (panel-only), so DB creation can't be scripted there. - DreamHost shared has no wildcard SSL; its managed Let's Encrypt only covers fully-hosted domains (HTTP-01, no DNS-01). - A VPS restores: own web server (on-demand/wildcard TLS), own MySQL (scriptable CREATE DATABASE -> real per-client DB isolation), and true one-script spin-up. ## Scope / tasks - [ ] Provision VPS (evaluate DreamCompute vs Hetzner vs DigitalOcean): Ubuntu, MySQL 8, PHP, web server - [ ] Reverse proxy with on-demand TLS (Caddy or Traefik) + authorization endpoint gating allowed hostnames - [ ] Single shared Joomla 6 install; finalize document-root strategy - [ ] Dynamic configuration.php resolving tenant by HTTP_HOST - [ ] Custom defines.php + administrator/defines.php for per-tenant cache/tmp/log/images - [ ] Tenant registry (flat tenants.php vs control-plane DB vs small management component): host -> {db creds, paths, secret, role (dev/prod), offline flag} - [ ] Domain-aware soft-offline: dev host always serves; prod host short-circuits to a "coming soon" page when flagged - [ ] Provisioning script: create DB + user, seed Joomla schema, write tenant entry, create writable dirs, fire DNS record via DreamHost DNS API - [ ] Per-tenant media isolation (images path per tenant) - [ ] Per-tenant DB schema migration runner (file updates once, DB migrations N times) - [ ] Per-tenant backup/restore (Borg) + monitoring hooks - [ ] De-provisioning / offboarding flow ## Open questions - DB-per-client vs single-DB-prefix on the VPS (leaning per-client now that DB creation is scriptable) - dev -> prod promotion: same DB with a flag, or separate dev/prod DBs with a content clone at launch? - Tenant registry: flat PHP file vs control-plane database vs management component - Client-owned domains vs platform subdomains (affects SSL: on-demand per-domain vs single wildcard)
Author
Owner

Branch created: feature/185-architecture-multi-tenant-provisioning-s

git fetch origin
git checkout feature/185-architecture-multi-tenant-provisioning-s
Branch created: [`feature/185-architecture-multi-tenant-provisioning-s`](https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/src/branch/feature/185-architecture-multi-tenant-provisioning-s) ```bash git fetch origin git checkout feature/185-architecture-multi-tenant-provisioning-s ```
Author
Owner

This is a v5.x milestone item — not in scope for the current 02.34.x release cycle. Parking for future implementation when the VPS provisioning infrastructure is ready.

This is a v5.x milestone item — not in scope for the current 02.34.x release cycle. Parking for future implementation when the VPS provisioning infrastructure is ready.
jmiller added this to the v05.00.00 (Multi-tenant) milestone 2026-06-06 16:17:52 +00:00
Sign in to join this conversation.
No labels
Priority Medium
Type Enhancement
Status
Priority
Type
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: MokoConsulting/MokoSuite#185