{"id":"69bb38f53f1c7d8bc1820a09","title":"Current Architecture","path":"how-to/technical-articles/2026-03-18-single-instance-multi-tenant-exploration/current-architecture","contentMarkdown":"# Current Architecture: Per-Group Deployment Model\n\n_____\n\nThe current NGX Ramblers architecture deploys a single Docker image to multiple independent Fly.io apps. Each app connects to its own MongoDB database and S3 bucket, providing complete data isolation.\n\n## Architecture Diagram\n\n```mermaid\nflowchart TD\n    IMG@{ icon: \"logos:docker-icon\", label: \"Single Docker Image\", pos: \"b\", h: 48 }\n\n    IMG --> APP1@{ icon: \"logos:fly-icon\", label: \"Group A (512MB)\", pos: \"b\", h: 48 }\n    IMG --> APP2@{ icon: \"logos:fly-icon\", label: \"Group B (512MB)\", pos: \"b\", h: 48 }\n    IMG --> APP3@{ icon: \"logos:fly-icon\", label: \"Group C (512MB)\", pos: \"b\", h: 48 }\n    IMG --> APPN[\"... x 13 apps\"]\n\n    APP1 --> DB1@{ icon: \"ngx:mongodb\", label: \"Group A DB\", pos: \"b\", h: 48 }\n    APP2 --> DB2@{ icon: \"ngx:mongodb\", label: \"Group B DB\", pos: \"b\", h: 48 }\n    APP3 --> DB3@{ icon: \"ngx:mongodb\", label: \"Group C DB\", pos: \"b\", h: 48 }\n\n    APP1 --> S31@{ icon: \"ngx:aws\", label: \"Group A bucket\", pos: \"b\", h: 48 }\n    APP2 --> S32@{ icon: \"ngx:aws\", label: \"Group B bucket\", pos: \"b\", h: 48 }\n    APP3 --> S33@{ icon: \"ngx:aws\", label: \"Group C bucket\", pos: \"b\", h: 48 }\n```\n\n## Key Characteristics\n\n- **13 separate Fly.io apps**, each with 512MB–1GB memory\n- **One MongoDB database per group** (complete data isolation)\n- **One S3 bucket per group** (separate image/file storage)\n- **One AUTH_SECRET per group** (independent JWT signing)\n- **One Cloudflare subdomain per group** (e.g. `group-a.ngx-ramblers.org.uk`)\n- Groups also have custom domains (e.g. `group-a-ramblers.org.uk`)\n- The Angular frontend is identical across all sites — all site-specific behaviour comes from runtime config loaded from the database\n\n## What Already Works\n\n| Capability | Status |\n|------------|--------|\n| Single codebase | One repo, one team |\n| Single build artifact | One Docker image for all sites |\n| Automated group provisioning | CLI tool creates new groups end-to-end |\n| Per-group custom domains | Each group has its own domain and branding |\n| Complete data isolation | Physically separate databases — no cross-group leakage possible |\n\n## Strengths of This Model\n\n- **Data isolation is inherent** — no risk of cross-group data leakage because databases are physically separate\n- **Blast radius is limited** — a bug or outage on one group's app doesn't affect others\n- **Resource isolation** — one group's heavy traffic doesn't starve others\n- **Simple application code** — no tenant context needed in queries, middleware, or auth\n","contentHtml":"<h1>Current Architecture: Per-Group Deployment Model</h1>\n<hr>\n<p>The current NGX Ramblers architecture deploys a single Docker image to multiple independent Fly.io apps. Each app connects to its own MongoDB database and S3 bucket, providing complete data isolation.</p>\n<h2>Architecture Diagram</h2>\n<pre><code class=\"language-mermaid\">flowchart TD\n    IMG@{ icon: &quot;logos:docker-icon&quot;, label: &quot;Single Docker Image&quot;, pos: &quot;b&quot;, h: 48 }\n\n    IMG --&gt; APP1@{ icon: &quot;logos:fly-icon&quot;, label: &quot;Group A (512MB)&quot;, pos: &quot;b&quot;, h: 48 }\n    IMG --&gt; APP2@{ icon: &quot;logos:fly-icon&quot;, label: &quot;Group B (512MB)&quot;, pos: &quot;b&quot;, h: 48 }\n    IMG --&gt; APP3@{ icon: &quot;logos:fly-icon&quot;, label: &quot;Group C (512MB)&quot;, pos: &quot;b&quot;, h: 48 }\n    IMG --&gt; APPN[&quot;... x 13 apps&quot;]\n\n    APP1 --&gt; DB1@{ icon: &quot;ngx:mongodb&quot;, label: &quot;Group A DB&quot;, pos: &quot;b&quot;, h: 48 }\n    APP2 --&gt; DB2@{ icon: &quot;ngx:mongodb&quot;, label: &quot;Group B DB&quot;, pos: &quot;b&quot;, h: 48 }\n    APP3 --&gt; DB3@{ icon: &quot;ngx:mongodb&quot;, label: &quot;Group C DB&quot;, pos: &quot;b&quot;, h: 48 }\n\n    APP1 --&gt; S31@{ icon: &quot;ngx:aws&quot;, label: &quot;Group A bucket&quot;, pos: &quot;b&quot;, h: 48 }\n    APP2 --&gt; S32@{ icon: &quot;ngx:aws&quot;, label: &quot;Group B bucket&quot;, pos: &quot;b&quot;, h: 48 }\n    APP3 --&gt; S33@{ icon: &quot;ngx:aws&quot;, label: &quot;Group C bucket&quot;, pos: &quot;b&quot;, h: 48 }\n</code></pre>\n<h2>Key Characteristics</h2>\n<ul>\n<li><strong>13 separate Fly.io apps</strong>, each with 512MB–1GB memory</li>\n<li><strong>One MongoDB database per group</strong> (complete data isolation)</li>\n<li><strong>One S3 bucket per group</strong> (separate image/file storage)</li>\n<li><strong>One AUTH_SECRET per group</strong> (independent JWT signing)</li>\n<li><strong>One Cloudflare subdomain per group</strong> (e.g. <code>group-a.ngx-ramblers.org.uk</code>)</li>\n<li>Groups also have custom domains (e.g. <code>group-a-ramblers.org.uk</code>)</li>\n<li>The Angular frontend is identical across all sites — all site-specific behaviour comes from runtime config loaded from the database</li>\n</ul>\n<h2>What Already Works</h2>\n<table>\n<thead>\n<tr>\n<th>Capability</th>\n<th>Status</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>Single codebase</td>\n<td>One repo, one team</td>\n</tr>\n<tr>\n<td>Single build artifact</td>\n<td>One Docker image for all sites</td>\n</tr>\n<tr>\n<td>Automated group provisioning</td>\n<td>CLI tool creates new groups end-to-end</td>\n</tr>\n<tr>\n<td>Per-group custom domains</td>\n<td>Each group has its own domain and branding</td>\n</tr>\n<tr>\n<td>Complete data isolation</td>\n<td>Physically separate databases — no cross-group leakage possible</td>\n</tr>\n</tbody></table>\n<h2>Strengths of This Model</h2>\n<ul>\n<li><strong>Data isolation is inherent</strong> — no risk of cross-group data leakage because databases are physically separate</li>\n<li><strong>Blast radius is limited</strong> — a bug or outage on one group&#39;s app doesn&#39;t affect others</li>\n<li><strong>Resource isolation</strong> — one group&#39;s heavy traffic doesn&#39;t starve others</li>\n<li><strong>Simple application code</strong> — no tenant context needed in queries, middleware, or auth</li>\n</ul>\n"}