FIKR.SPACE LIVE · Q3 2026 LAUNCH
FOUNDING / 200 JOIN FOUNDING
Home/Blog/AI Software Development/Multi-tenant SSR: One Worker, Infinite O…
AI Software Development

Multi-tenant SSR: One Worker, Infinite Organizations

How we architected a single Cloudflare Worker to serve 1000+ organizations with complete data isolation, per-tenant caching, and sub-40ms response times at $5/month.

How we architected a single Cloudflare Worker to serve 1000+ organizations with complete data isolation, per-tenant caching, and sub-40ms response times at $5/month

The Problem: One Codebase, Thousands of Tenants

When we finished implementing Edge SSR for FIKR, we immediately faced a new architectural challenge that would test everything we thought we knew about multi-tenant applications. FIKR isn't a single website - it's a platform that hosts thousands of independent organizations. Each organization has its own custom domain, branding, content, and data. Some organizations are venture capital firms showcasing their portfolio companies. Others are accelerator programs managing cohorts of startups. Still others are corporate innovation labs tracking internal projects.

The traditional approach to multi-tenancy would be to deploy separate instances for each organization. Each tenant gets their own server, their own database, and their own infrastructure. This provides excellent isolation, but it's expensive and complex to manage. With 1000 organizations, you're managing 1000 deployments, 1000 sets of environment variables, and 1000 potential points of failure.

The alternative is a shared infrastructure model where a single application serves all tenants. This is much more efficient, but it introduces critical challenges. How do you identify which organization a request belongs to? How do you ensure Organization A can't accidentally see Organization B's data? How do you cache content without mixing data between tenants? And most importantly, how do you do all of this at edge scale with sub-50ms response times?

We needed a solution that could handle organization resolution from hostnames, enforce strict data isolation between tenants, implement per-tenant caching strategies, and scale effortlessly to thousands of organizations without breaking the bank. The answer was a carefully designed multi-tenant architecture built on Cloudflare Workers that processes every request through a single edge function while maintaining perfect data isolation.

THE COMPLETE SYSTEM TO SCALE YOUR STARTUP

From strategic planning to execution, FIKR gives you every tool needed to grow from idea to exit.

START FOR FREE →

No credit card required • Setup in 5 minutes

Understanding Multi-tenant Edge Architecture

Multi-tenancy at the edge fundamentally changes how you think about request routing and data isolation. In a traditional server-based application, you might use middleware to detect the tenant and load their configuration. At the edge, you need to make those decisions in milliseconds, with minimal database queries, and perfect reliability. Every request that hits our Cloudflare Worker goes through a precise identification and isolation flow.

Here's what happens when someone requests a page from any organization on our platform:

  1. Request arrives with hostname (e.g., acme-vc.fikr.space or custom.domain.com)
  2. Edge Worker extracts hostname and checks Workers KV cache for organization metadata
  3. If cached, organization context loads in <5ms; if not, fetches from Supabase (~40ms)
  4. Worker validates organization status (active, suspended, trial) and enforces access rules
  5. All subsequent database queries are scoped to this organization's UUID
  6. Content renders with organization-specific branding, content, and configuration
  7. Response cached in KV with tenant-specific key to prevent cross-contamination

The elegance of this approach is that every organization gets the same fast, reliable experience regardless of their scale. A small startup with 10 pages gets the same sub-40ms response times as an enterprise accelerator with 10,000 pages. The edge infrastructure scales horizontally across Cloudflare's 300+ locations without any configuration on our part. And because we're using a shared codebase, every bug fix and feature enhancement immediately benefits all tenants.

But the real magic is in the caching strategy. We can't just cache content globally because that would mix data between organizations. Instead, we implement tenant-aware caching where every cache key includes the organization UUID. When Organization A updates their homepage, only their cache invalidates. Organization B's cached content remains untouched. This gives us the performance benefits of aggressive caching without the complexity of cache invalidation across tenants.

Organization Resolution from Hostname

The cornerstone of multi-tenant architecture is reliably identifying which organization a request belongs to. We support three types of hostname patterns: subdomain-based routing (organization.fikr.space), custom domains (www.organization.com), and multi-level subdomains (team.organization.fikr.space). Each pattern requires different resolution logic, but they all need to execute in under 5 milliseconds to maintain our performance targets.

For subdomain-based routing on fikr.space, the logic is straightforward. We extract the first subdomain segment and use it as the organization slug. For example, acme-vc.fikr.space becomes the slug "acme-vc", which we look up in our organization registry. This lookup happens against Workers KV, which stores a mapping of slugs to organization UUIDs and basic metadata.

Custom domains are more complex because we need to maintain a registry of which domains belong to which organizations. When an organization adds a custom domain through our admin interface, we store that mapping in Supabase and immediately push it to Workers KV. The edge worker checks incoming hostnames against this registry. If custom.domain.com is registered to Organization ABC, all requests to that hostname load Organization ABC's content.

Hostname Resolution Implementation:

// Extract hostname from request
const url = new URL(request.url);
const hostname = url.hostname;

// Check if this is a custom domain
let orgId = await CUSTOM_DOMAINS.get(hostname);

// If not a custom domain, try subdomain resolution
if (!orgId) {
  const subdomain = hostname.split('.')[0];
  orgId = await ORGANIZATION_SLUGS.get(subdomain);
}

// Validate organization exists and is active
const orgData = await getOrganizationData(orgId);
if (!orgData || orgData.status !== 'active') {
  return new Response('Organization not found', { status: 404 });
}

// Attach organization context to request
request.orgId = orgData.id;
request.orgSlug = orgData.slug;
request.orgConfig = orgData.config;

This resolution happens before any content rendering or database queries. Once we have the organization context, every subsequent operation is scoped to that tenant. Database queries include WHERE organization_id = X filters. Cache keys are prefixed with the organization UUID. Authentication checks validate that users have access to this specific organization. This approach provides defense-in-depth against data leakage.

Why We Didn't Choose Separate Deployments

The most obvious approach to multi-tenancy is giving each organization their own deployment. Many successful SaaS companies use this model, and it has real benefits. Each tenant is completely isolated at the infrastructure level. There's zero risk of data leakage between tenants. And you can customize deployments per customer, offering premium features or configurations to high-value accounts.

But for a platform serving 1000+ organizations with varying scales, separate deployments would be a operational nightmare and a financial disaster. Every organization would need their own Cloudflare Worker deployment, their own environment configuration, and their own monitoring setup. A critical bug fix would require 1000 separate deployments. A new feature would need to be rolled out 1000 times. The operational overhead would consume our entire engineering team.

Here are the key factors that made us choose shared infrastructure:

  • Deployment Complexity: 1 deployment vs 1000 separate deployments to manage
  • Operational Overhead: Single monitoring dashboard instead of 1000 separate systems
  • Cost Efficiency: $5/month total vs $5/month per organization ($5,000/month)
  • Feature Velocity: Ship features once and all organizations benefit immediately
  • Infrastructure Scaling: Cloudflare handles scaling automatically across all tenants
  • Bug Fixes: One fix instantly deployed to all organizations

The shared infrastructure model also enables features that would be impossible with separate deployments. We can analyze usage patterns across all organizations to identify common workflows and build better features. We can offer cross-organization discovery features where startups can find relevant investors across our entire network. And we can implement global search that spans multiple organizations (with proper permission controls).

THE COMPLETE SYSTEM TO SCALE YOUR STARTUP

From strategic planning to execution, FIKR gives you every tool needed to grow from idea to exit.

START FOR FREE →

No credit card required • Setup in 5 minutes

Data Isolation Architecture

Data isolation in a multi-tenant system is non-negotiable. A single bug that allows Organization A to see Organization B's data would be catastrophic. We implement defense-in-depth with four layers of isolation: database-level row-level security, application-level filtering, cache key namespacing, and runtime validation.

At the database level, we use Supabase's Row-Level Security (RLS) policies to enforce organization boundaries. Every table that contains tenant-specific data has an organization_id column, and RLS policies ensure that queries can only access rows matching the authenticated organization. Even if our application code has a bug and forgets to filter by organization, the database will reject the query.

At the application level, every database query explicitly filters by organization ID. We never rely on RLS alone - our code always includes WHERE organization_id = X in queries. This provides a second layer of validation and makes our queries more efficient because the database can use indexes on the organization_id column.

Database Query Pattern with Organization Scoping:

// Load organization context from hostname resolution
const orgId = request.orgId;

// Every database query includes explicit organization filter
const { data: pages, error } = await supabase
  .from('pages')
  .select('*')
  .eq('organization_id', orgId)
  .eq('slug', pageSlug)
  .single();

// Validate data belongs to this organization (paranoid check)
if (pages && pages.organization_id !== orgId) {
  throw new Error('Data isolation violation detected');
}

// Safe to use the data - it's guaranteed to belong to this org
return renderPage(pages, orgConfig);

Cache isolation is equally critical. We use Workers KV for caching organization metadata, page content, and configuration data. Every cache key includes the organization UUID as a prefix. When we cache Organization A's homepage, it goes into KV with the key org:abc-123:page:home. Organization B's homepage uses key org:xyz-789:page:home. There's no possibility of cache collision or data leakage between tenants.

The final layer is runtime validation. Before rendering any content, we verify that the loaded data matches the expected organization ID. This catches any bugs in our resolution logic or data loading code. It's redundant with our other layers, but redundancy is exactly what you want when protecting against data leakage.

Per-tenant Caching Strategies

Caching in a multi-tenant system requires careful thought about invalidation, isolation, and efficiency. We cache aggressively because edge response times depend on minimizing database queries. But we need to ensure that when Organization A updates their content, it doesn't affect Organization B's cached data, and that stale content doesn't persist longer than appropriate.

Our caching strategy uses three tiers with different TTLs and invalidation rules. Organization metadata (name, slug, status, configuration) is cached for 1 hour because it rarely changes. When it does change, we explicitly invalidate the cache key. This gives us fast hostname resolution without hitting the database on every request.

Page content is cached for 5 minutes with stale-while-revalidate. When a bot requests a page, we serve the cached version if it exists and asynchronously fetch fresh data in the background. This ensures bots always get fast responses while still receiving updated content within minutes of any changes. If an organization explicitly purges their cache, we invalidate all their page content keys immediately.

User-specific data (personalized dashboards, private pages) is never cached in Workers KV. These requests always hit the database because caching user-specific content would require exponentially more cache storage and complex invalidation logic. Instead, we rely on Supabase's internal caching and query optimization to keep these requests fast.

Cache key structure is critical for isolation and invalidation. We use a hierarchical naming scheme:

  • Organization metadata: org:{orgId}:meta
  • Page content: org:{orgId}:page:{slug}
  • Navigation menu: org:{orgId}:nav
  • Blog posts: org:{orgId}:blog:{postSlug}
  • Search index: org:{orgId}:search:v1

This structure lets us invalidate at different granularities. We can purge all cache for an organization by deleting everything with prefix org:{orgId}:. We can invalidate just pages by targeting org:{orgId}:page:*. Or we can surgically invalidate a single page by deleting its specific key. This flexibility is essential for maintaining cache efficiency without serving stale content.

Real Results Across 200 Organizations

Six months after launching our multi-tenant Edge SSR architecture, we're now serving 200+ active organizations with thousands of pages each. The infrastructure handles everything from small startup accelerators with 10 pages to large venture capital firms with 5,000+ portfolio company profiles. Performance has remained consistent regardless of tenant size or traffic patterns.

The metrics tell a compelling story about the architecture's scalability and efficiency:

Before Multi-tenant Edge SSR:

  • Organizations served: 1 (FIKR only)
  • Custom domains supported: 0
  • Average response time: 180ms (single-tenant optimization)
  • Cache hit rate: 65%
  • Infrastructure cost: $5/month

After Multi-tenant Edge SSR (6 months):

  • Organizations served: 200+ active tenants
  • Custom domains: 45 custom domains configured
  • Pages served: 850,000+ pages across all organizations
  • Average response time: 38ms globally (95th percentile: 62ms)
  • Cache hit rate: 89% (improved with per-tenant caching)
  • Data isolation incidents: 0 (perfect isolation maintained)
  • Infrastructure cost: $5/month (unchanged)

The performance improvement from 180ms to 38ms came from optimizing our caching strategy and minimizing database queries through aggressive use of Workers KV. The cache hit rate jumped from 65% to 89% because per-tenant caching allows much longer TTLs without risk of serving stale content to the wrong organization.

Most importantly, we've maintained perfect data isolation across 200 organizations over six months of production traffic. Zero incidents of cross-tenant data leakage. Zero cache contamination issues. The architecture's defensive layers have proven robust under real-world conditions.

Challenges We Had to Overcome

Building a production-grade multi-tenant edge architecture required solving several non-obvious problems. The first major challenge was handling organization onboarding and DNS configuration. When a new organization signs up and wants to use a custom domain, they need to configure DNS records pointing to our Cloudflare Workers. But DNS propagation can take minutes to hours, and during that time, requests might be routed incorrectly.

We solved this with a staging and verification flow:

  • Organization adds custom domain in admin UI and gets DNS instructions
  • Our system polls DNS records every 5 minutes checking for correct configuration
  • Once DNS is verified, we immediately push domain mapping to Workers KV
  • Edge worker checks both verified and pending domains to handle propagation delays
  • Organization sees real-time status updates as DNS propagates globally

The second challenge was managing cache invalidation at scale. With 200 organizations and 850,000 pages, we couldn't manually invalidate cache for every content change. We needed intelligent invalidation that respects organization boundaries while minimizing unnecessary cache purges.

We built an event-driven cache invalidation system:

  • Content changes trigger database events (Supabase webhooks)
  • Webhook handler identifies affected cache keys by organization and content type
  • Batch delete operations purge relevant cache entries from Workers KV
  • Edge workers automatically rebuild cache on next request
  • Result: Content updates visible in <30 seconds globally

The third challenge was handling organizations at wildly different scales. Some organizations have 10 pages with 100 monthly visitors. Others have 5,000 pages with 100,000 monthly visitors. We needed to ensure our caching and database query patterns didn't penalize small organizations or get overwhelmed by large ones.

The solution was adaptive caching with usage-based TTLs. High-traffic pages automatically get longer cache TTLs and more aggressive prefetching. Low-traffic pages use shorter TTLs to conserve KV storage. The system monitors cache hit rates per organization and automatically adjusts TTLs to optimize for both performance and cost.

Scaling to 1000+ Organizations

Our current architecture comfortably handles 200 organizations, but we designed it to scale to 10,000+ without fundamental changes. The key insight is that edge computing resources scale horizontally by default. When Organization 201 signs up, they don't consume a fixed slice of resources - their requests are distributed across Cloudflare's global network just like everyone else's.

The bottleneck isn't compute capacity or network bandwidth. It's database queries and cache storage. We've optimized both to scale linearly with organization count rather than exponentially. Each organization's metadata is cached independently, so adding more organizations doesn't slow down lookup times. Database queries are indexed by organization_id, so adding more organizations doesn't impact query performance as long as we're not scanning across tenants.

Workers KV is our cache layer, and it scales to billions of keys. At 200 organizations with average 4,000 pages each, we're storing roughly 800,000 cache entries. At 1,000 organizations, that's 4 million entries. At 10,000 organizations, it's 40 million entries. KV pricing is $0.50 per million reads and $5 per million writes. Even at 40 million cached entries with moderate turnover, we're looking at $50-100/month in KV costs. Still a fraction of what separate deployments would cost.

The real scaling test will be custom domains. We currently support 45 custom domains across 200 organizations. At 1,000 organizations with 25% using custom domains, that's 250 domain mappings. Cloudflare's DNS infrastructure handles this trivially. The operational challenge is customer support - helping organizations configure DNS correctly and troubleshooting SSL certificate issues. That scales with headcount, not with infrastructure.

When Multi-tenant Makes Sense vs Separate Deployments

Multi-tenant edge architecture isn't always the right choice. The decision depends on your specific requirements, scale, and compliance constraints. We've learned when each approach makes sense based on real-world trade-offs.

Choose Multi-tenant Edge Architecture if:

  • You're serving 100+ organizations with similar features and data models
  • Organizations have relatively consistent scale (no 1000x differences)
  • You need to ship features and fixes quickly across all tenants
  • Cost efficiency matters and you're optimizing for operational simplicity
  • Data isolation can be enforced through organization IDs and RLS policies
  • Custom branding per tenant is mostly configuration-driven (colors, logos, domains)
  • You don't need per-tenant infrastructure customization (special regions, compliance rules)

Choose Separate Deployments if:

  • You have fewer than 20 organizations, especially if they're high-value enterprise customers
  • Different tenants have wildly different scale (one has 10x the traffic of all others)
  • Compliance requires physical separation (healthcare, finance, government data)
  • Customers demand dedicated infrastructure in specific regions or with specific configurations
  • You need to offer vastly different feature sets per tenant (white-labeling with custom code)
  • Risk tolerance for data isolation issues is zero (defense-in-depth isn't enough)
  • You have operational capacity to manage many deployments (large DevOps team)

For our use case - a platform serving hundreds of organizations with similar needs, consistent scale, and configuration-driven customization - multi-tenant edge architecture was clearly the right choice. We get the cost efficiency of shared infrastructure, the operational simplicity of single deployments, and the performance benefits of edge computing.

Final Thoughts

Building a multi-tenant edge architecture has been one of the most impactful technical decisions we've made at FIKR. Six months in, we're serving 200+ organizations, handling 850,000+ pages, maintaining sub-40ms response times globally, and spending $5 per month on infrastructure. The architecture scales effortlessly as we add new organizations, and every improvement benefits all tenants simultaneously.

The key to success was embracing edge computing's strengths while designing defensive layers for data isolation. Cloudflare Workers give us global scale and low latency automatically. Workers KV provides fast, tenant-aware caching. And careful attention to organization resolution, database filtering, and cache key namespacing ensures perfect data isolation despite shared infrastructure.

The development effort was surprisingly modest. We spent about three weeks designing the architecture, implementing organization resolution logic, building the caching layer, and testing data isolation. Most of that time was spent on defensive programming and validation logic - ensuring we had multiple layers of protection against data leakage. The actual rendering logic barely changed from our single-tenant implementation.

If you're building a multi-tenant application and evaluating infrastructure options, consider edge computing carefully. The combination of global scale, low latency, and cost efficiency is hard to beat. And with careful architecture around organization resolution and data isolation, you can serve thousands of tenants from a single deployment without compromising on security or performance.

THE COMPLETE SYSTEM TO SCALE YOUR STARTUP

From strategic planning to execution, FIKR gives you every tool needed to grow from idea to exit.

START FOR FREE →

No credit card required • Setup in 5 minutes

LG

Luis Goncalves

// Founder & CEO at FIKR Space

Three-time founder. Built and exited Evolution4All before this. Now building FIKR Space — the operating infrastructure underneath every innovation ecosystem (startups, accelerators, governments, investors). Lisbon-based, works global.