System Overview
Mezzanine runs across three servers with distinct jobs: the App box hosts MYACCESS’s own panel, the SaaS box runs the hosted multi-tenant product, and the Development box is the deploy target for MYACCESS’s portfolio apps. This page is the map — start here, then drill into each box’s architecture and operator guide.
The three boxes at a glance
Each box is its own Docker host. Public web traffic reaches an application container only through that box’s edge proxy: mezzanine-nginx on the App box, mezzanine-traefik on the SaaS box, and the system nginx on the Development box. Every other container listens on the Docker network only.
graph LR
U["End users (browsers)"]
subgraph appbox["App box 5.189.154.35 (docker_default)"]
AN["mezzanine-nginx (publishes 80 and 443)"]
AF["mezzanine-frontend (3000, internal)"]
AB["mezzanine-backend (3001, internal)"]
AN -->|"/ "| AF
AN -->|"/api plus Socket.IO websockets"| AB
end
subgraph saasbox["SaaS box 62.171.156.201 (mezzanine-tenants)"]
TR["mezzanine-traefik (publishes 80 and 443)"]
CP["mezzanine-control-plane (4000)"]
T1["mz-slug-frontend (3000)"]
T2["mz-slug-backend (internal)"]
TR -->|"try.mezzanine.cloud"| CP
TR -->|"slug.mezzanine.cloud, by labels"| T1
T1 -->|"/api on docker net"| T2
end
subgraph devbox["Development box 192.210.241.34 (host, no Mezzanine app)"]
SN["System nginx plus certbot"]
APP["App containers (e.g. investors, 8090)"]
SN --> APP
end
U -->|"app, docs, marketing"| AN
U -->|"try and tenant subdomains"| TR
U -->|"portfolio app domains"| SN
DNS routes clients to the right box
DNS is the splitter. The mezzanine.cloud zone lives on Cloudflare (which is also why traefik does DNS-01 with certresolver cf). Explicit app / docs / www / apex records point at the App box; the * wildcard — covering try. and every tenant slug — points at the SaaS box. Explicit records win over the wildcard, so the two Mezzanine boxes share one domain without interfering. Portfolio app domains resolve to the Development box via Hostinger — except myaccessio.com, whose nameservers point to Wix (so its DNS is managed at Wix, not Hostinger).
graph LR CF["Cloudflare DNS (mezzanine.cloud zone)"] HG["Hostinger DNS"] WIX["Wix DNS (myaccessio.com)"] AN["App box mezzanine-nginx"] TR["SaaS box mezzanine-traefik"] SN["Development box system nginx"] CF -->|"app, docs, www, apex"| AN CF -->|"wildcard star.mezzanine.cloud"| TR HG -->|"portfolio domains"| SN WIX -->|"myaccessio.com"| SN
A tour of the three boxes
App box (5.189.154.35) — MYACCESS’s own panel. A single Docker host on the docker_default bridge network. mezzanine-nginx (nginx:1.27-alpine) is the only container publishing ports (80/443); it terminates TLS (certbot/Let’s Encrypt on the host, certs mounted into the container) and reverse-proxies three vhosts: app.mezzanine.cloud (the panel — / to mezzanine-frontend:3000, /api plus Socket.IO websockets to mezzanine-backend:3001), mezzanine.cloud + www. (static marketing landing page from /var/www/mezzanine.cloud), and docs.mezzanine.cloud (this Astro docs site from /var/www/docs.mezzanine.cloud). All state is one named volume, mezzanine_data (the backend’s SQLite at /data/mezzanine.db plus uploads). See the Architecture page and the App box guide.
SaaS box (62.171.156.201) — the hosted multi-tenant product. On the mezzanine-tenants bridge network, mezzanine-traefik (traefik:v3) publishes 80/443 and does wildcard TLS for *.mezzanine.cloud via Cloudflare DNS-01 (certresolver cf). Routing is driven by labels on each tenant’s frontend container — traefik.enable=true, router rule Host(<slug>.mezzanine.cloud) on the websecure entrypoint, tls.certresolver=cf, and loadbalancer.server.port=3000 (plus mezzanine.tenant=<slug>). try.mezzanine.cloud routes to mezzanine-control-plane (port 4000) — the public signup site — which owns signup, accounts, billing (Razorpay, env-gated), the superadmin/ops console, the provisioner, and tenant lifecycle. Each tenant is its own mz-<slug>-frontend (mezzanine-frontend:latest, listens on 3000) + mz-<slug>-backend (mezzanine-backend:latest) pair with a tenant_<slug>_data volume; control-plane state lives in mezzanine_cp_data, with saas_cp_backups, saas_traefik_acme, and saas_traefik_logs alongside. Tenants hibernate when idle and wake on the next request — see the SaaS box guide for the lifecycle details (and the recreate-vs-rebuild gotcha).
Development box (192.210.241.34) — the deploy target. A plain Ubuntu box (not running the Mezzanine app) with system nginx + certbot installed directly on the host. MYACCESS’s portfolio apps (KiOSK, Investor profile, MYA ERP, 3D Farm Lab, main site) run as Docker containers here; system nginx reverse-proxies each app’s domain to its published port with Let’s Encrypt certs (e.g. the Investor app: investors:latest, docker run -d --name investors --restart=unless-stopped -p 8090:3000 -v /opt/investors-data:/app/data). Mezzanine manages this box entirely over SSH from the App box’s backend — that’s the cross-box deploy path below.
How they relate
The boxes are otherwise independent — no shared database; each has its own SQLite. The only cross-box link is operational: a developer pushes (or uses a linked folder push — the File System Access API panel commits only changed, gitignore-filtered files straight to the repo via the GitHub Git Data API), which fires a webhook into the App box backend, which then runs the deploy workflow against the Development box over SSH.
graph LR DEV["Developer (git push or linked-folder push)"] GH["GitHub repo (webhook source)"] AB["App box mezzanine-backend (workflow engine)"] SN["Development box 192.210.241.34 (over SSH)"] DEV -->|"commit"| GH GH -->|"HMAC-signed POST to /api/workflows/id/webhook"| AB AB -->|"Sync code, Build image, Run container (SSH)"| SN
git push / linked-folder push -> webhook to app.mezzanine.cloud/api/workflows/<id>/webhook (App box backend) -> workflow: Sync code -> Build image -> Run container (over SSH) -> Development box (192.210.241.34)Live run output streams over the Socket.IO /workflows namespace into an in-panel terminal (stages, status, durations). The same mezzanine-backend reaches managed servers like the Development box over SSH (ssh2) and reaches cloud providers (Hostinger, Contabo, Hetzner, DigitalOcean, Cloudflare) over their HTTPS REST APIs — see Provider integrations.
Summary table
| Box | Public IP | Role | Docker network | Public ports | Key containers |
|---|---|---|---|---|---|
| App box | 5.189.154.35 | MYACCESS’s own Mezzanine panel (single-tenant) | docker_default | 80, 443 (via mezzanine-nginx) | mezzanine-nginx (nginx:1.27-alpine), mezzanine-frontend (3000 internal), mezzanine-backend (3001 internal) |
| SaaS box | 62.171.156.201 | Hosted multi-tenant Mezzanine product | mezzanine-tenants | 80, 443 (via mezzanine-traefik) | mezzanine-traefik (traefik:v3), mezzanine-control-plane (4000), per-tenant mz-<slug>-frontend (3000) + mz-<slug>-backend (internal) |
| Development box | 192.210.241.34 | Deploy target for MYACCESS portfolio apps (managed over SSH) | host (no Mezzanine app) | 80, 443 (system nginx) + per-app published ports (e.g. 8090) | System nginx + certbot (host); app containers (e.g. investors:latest) |