Multi-Tenant SaaS Data Delivery at Scale

Use Case: Multi-Tenant SaaS Data Delivery at Scale

The Challenge: Serving Unique Content to Thousands of Tenants

Multi-tenant SaaS platforms face a unique scaling challenge: every customer needs personalized content delivered efficiently, but traditional approaches don’t scale:

Common Scenarios:

  • White-label SaaS platforms delivering branded assets to each client
  • Educational platforms providing course materials to thousands of schools
  • E-commerce platforms generating product exports for merchants
  • Marketing automation tools delivering campaign assets to agencies

Traditional Approach: Pre-generate and store content for each tenant

Example: White-label design tool with 10,000 tenants

Each tenant needs:

  • Custom branded templates (50MB)
  • Client-specific assets (100MB)
  • Shared core library (200MB)
  • Tenant configuration files (1MB)

Storage Requirements:

  • Pre-generated approach: 10,000 × 351MB = 3.5TB
  • Cost: ~$80/month on S3 (just for storage)
  • Maintenance: Re-generate when shared library updates
  • Scalability: Storage grows linearly with tenant count

The ZipStream Solution: Dynamic Multi-Tenant Content Assembly

ZipStream enables composable content delivery where shared resources and tenant-specific files are assembled on-demand, eliminating redundant storage.

Architecture

Request for Tenant A:
  Shared Library (S3) ──┐
  Tenant A Assets (S3) ─┼──→ ZipStream ──→ Custom ZIP for Tenant A
  Tenant A Config ──────┘

Request for Tenant B:
  Shared Library (S3) ──┐  (same shared library!)
  Tenant B Assets (S3) ─┼──→ ZipStream ──→ Custom ZIP for Tenant B
  Tenant B Config ──────┘

Implementation Example

// Express.js multi-tenant delivery
app.get('/api/tenants/:tenantId/download-package', async (req, res) => {
  const { tenantId } = req.params;

  // Verify tenant access
  const tenant = await db.getTenant(tenantId);
  if (!tenant) {
    return res.status(404).json({ error: 'Tenant not found' });
  }

  const files = [];

  // 1. Shared core library (same for all tenants)
  // Stored once, used by all 10,000 tenants
  const coreLibraryFiles = [
    {
      url: 'https://cdn.yourapp.com/shared/core-library-v2.3.zip',
      zipPath: 'lib/core.zip'
    },
    {
      url: 'https://cdn.yourapp.com/shared/templates-base.zip',
      zipPath: 'templates/base.zip'
    }
  ];
  files.push(...coreLibraryFiles);

  // 2. Tenant-specific branded assets
  const tenantAssets = await db.getTenantAssets(tenantId);
  for (const asset of tenantAssets) {
    const signedUrl = await s3.getSignedUrl('getObject', {
      Bucket: 'tenant-assets',
      Key: `${tenantId}/${asset.filename}`,
      Expires: 3600
    });
    files.push({
      url: signedUrl,
      zipPath: `assets/${asset.filename}`
    });
  }

  // 3. Dynamic configuration file
  const config = {
    tenantId: tenant.id,
    tenantName: tenant.name,
    branding: {
      primaryColor: tenant.primaryColor,
      logo: tenant.logoUrl,
      domain: tenant.customDomain
    },
    features: tenant.enabledFeatures,
    generatedAt: new Date().toISOString()
  };

  const configUrl = await uploadTemporaryJSON(config, `config-${tenantId}.json`);
  files.push({
    url: configUrl,
    zipPath: 'config.json'
  });

  // 4. Tenant-specific documentation
  const docs = generateTenantDocs(tenant);
  const docsUrl = await uploadTemporaryMarkdown(docs, `README-${tenantId}.md`);
  files.push({
    url: docsUrl,
    zipPath: 'README.md'
  });

  // 5. Create personalized package
  const zipStream = await fetch('https://zipstream.app/api/downloads', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      suggestedFilename: `${tenant.slug}-package-v${tenant.version}.zip`,
      files: files,
      compression: "STORE" // Assets are already compressed
    })
  });

  // Log download for analytics
  await logTenantDownload(tenantId, files.length);

  // Stream to tenant
  res.set('Content-Type', 'application/zip');
  res.set('Content-Disposition', `attachment; filename="${tenant.slug}-package.zip"`);
  zipStream.body.pipe(res);
});

Key Benefits

1. Massive Storage Savings

Example: 10,000 tenants, 200MB shared library, 50MB unique assets per tenant

Traditional (Pre-generated):

  • 10,000 × (200MB shared + 50MB unique) = 2.5TB
  • S3 Standard: ~$57/month
  • Update overhead: Must regenerate all 10,000 packages when library updates

ZipStream (On-Demand):

  • Shared library: 200MB × 1 = 200MB
  • Unique assets: 50MB × 10,000 = 500GB
  • Total: 500.2GB
  • S3 Standard: ~$11.50/month
  • Savings: $45.50/month (80% reduction)
  • Update overhead: Zero (packages generated on-demand)

2. Instant Updates

When shared library updates:

  • Traditional: Regenerate 10,000 packages (hours of compute)
  • ZipStream: Update one file, all future downloads use new version instantly

3. Version Management

// Support multiple versions simultaneously
app.get('/api/tenants/:tenantId/download/:version', async (req, res) => {
  const { tenantId, version } = req.params;

  const files = [];

  // Different core library versions
  if (version === 'v2') {
    files.push({
      url: 'https://cdn.yourapp.com/shared/core-library-v2.3.zip',
      zipPath: 'lib/core.zip'
    });
  } else if (version === 'v3') {
    files.push({
      url: 'https://cdn.yourapp.com/shared/core-library-v3.0.zip',
      zipPath: 'lib/core.zip'
    });
  }

  // Tenant assets (same across versions)
  // ... add tenant-specific files

  // Create version-specific package
  const zipStream = await createZipStream(files, `${tenantId}-${version}.zip`);
  zipStream.body.pipe(res);
});

Benefit: Support legacy versions without duplicating storage

Real-World Example: Educational Platform

Case Study: Online learning platform with 5,000 schools

Each school needs:

  • Course materials (shared): 2GB
  • School-specific branding: 10MB
  • Student roster files: 5MB
  • Custom assignments: 20MB

Before ZipStream:

  • Pre-generated packages: 5,000 × 2.035GB = 10TB
  • S3 cost: ~$230/month
  • Re-generation time (when course updates): 8 hours
  • Re-generation frequency: Weekly
  • Compute cost: ~$50/month

After ZipStream:

  • Shared courses: 2GB × 1 = 2GB
  • School-specific: 35MB × 5,000 = 175GB
  • Total: 177GB
  • S3 cost: ~$4/month
  • Update time: 0 seconds (just upload new course files)
  • Compute cost: ~$5/month
  • Total savings: $271/month (95% reduction)

Advanced Pattern: Conditional Content Assembly

// Assemble different packages based on tenant tier
app.get('/api/tenants/:tenantId/download', async (req, res) => {
  const tenant = await db.getTenant(req.params.tenantId);
  const files = [];

  // Core library (all tiers)
  files.push({
    url: 'https://cdn.yourapp.com/shared/core.zip',
    zipPath: 'lib/core.zip'
  });

  // Premium features (premium tier only)
  if (tenant.tier === 'premium' || tenant.tier === 'enterprise') {
    files.push({
      url: 'https://cdn.yourapp.com/shared/premium-features.zip',
      zipPath: 'lib/premium.zip'
    });
  }

  // Enterprise integrations (enterprise only)
  if (tenant.tier === 'enterprise') {
    files.push({
      url: 'https://cdn.yourapp.com/shared/enterprise-connectors.zip',
      zipPath: 'lib/enterprise.zip'
    });

    // Add tenant-specific SSO configuration
    const ssoConfig = await generateSSOConfig(tenant);
    const ssoUrl = await uploadTemporary(ssoConfig);
    files.push({
      url: ssoUrl,
      zipPath: 'config/sso.xml'
    });
  }

  // Add-ons (based on enabled features)
  if (tenant.enabledFeatures.includes('analytics')) {
    files.push({
      url: 'https://cdn.yourapp.com/addons/analytics-dashboard.zip',
      zipPath: 'addons/analytics.zip'
    });
  }

  if (tenant.enabledFeatures.includes('automation')) {
    files.push({
      url: 'https://cdn.yourapp.com/addons/automation-toolkit.zip',
      zipPath: 'addons/automation.zip'
    });
  }

  // Tenant-specific customizations
  const customizations = await getTenantCustomizations(tenant.id);
  for (const custom of customizations) {
    files.push({
      url: custom.url,
      zipPath: `custom/${custom.name}`
    });
  }

  // Generate package
  const zipStream = await createZipStream(
    files,
    `${tenant.slug}-${tenant.tier}-package.zip`
  );

  zipStream.body.pipe(res);
});

Benefit: Thousands of possible combinations without storing each permutation

White-Label SaaS Example

// Deliver fully branded packages to white-label customers
app.get('/api/white-label/:partnerId/installer', async (req, res) => {
  const { partnerId } = req.params;
  const partner = await db.getPartner(partnerId);

  const files = [];

  // 1. Core application (same for all partners)
  files.push({
    url: 'https://cdn.yourapp.com/releases/app-v1.5.0.exe',
    zipPath: 'installer/app.exe'
  });

  // 2. Partner-specific branding
  files.push({
    url: partner.brandedSplashScreen,
    zipPath: 'installer/splash.png'
  });
  files.push({
    url: partner.customIcon,
    zipPath: 'installer/icon.ico'
  });

  // 3. Dynamic configuration with partner details
  const installerConfig = {
    appName: partner.appName,
    publisherName: partner.companyName,
    supportUrl: partner.supportUrl,
    apiEndpoint: partner.customApiEndpoint || 'https://api.yourapp.com',
    branding: {
      primaryColor: partner.primaryColor,
      secondaryColor: partner.secondaryColor,
      fontFamily: partner.fontFamily
    }
  };

  const configUrl = await uploadTemporaryJSON(installerConfig);
  files.push({
    url: configUrl,
    zipPath: 'installer/config.json'
  });

  // 4. Partner-specific EULA
  const eulaUrl = await generatePartnerEULA(partner);
  files.push({
    url: eulaUrl,
    zipPath: 'EULA.txt'
  });

  // 5. Installation guide with partner branding
  const guideUrl = await generateBrandedGuide(partner);
  files.push({
    url: guideUrl,
    zipPath: 'Installation_Guide.pdf'
  });

  // Create white-labeled installer package
  const zipStream = await fetch('https://zipstream.app/api/downloads', {
    method: 'POST',
    body: JSON.stringify({
      suggestedFilename: `${partner.slug}-installer-v${partner.version}.zip`,
      files: files,
      compression: "DEFLATE"
    })
  });

  zipStream.body.pipe(res);
});

Multi-Region Content Delivery

// Optimize delivery by selecting closest CDN region
app.get('/api/tenants/:tenantId/download', async (req, res) => {
  const tenantId = req.params.tenantId;
  const userRegion = detectUserRegion(req); // Based on IP or headers

  // Select CDN endpoint closest to user
  const cdnBaseUrl = {
    'us-east': 'https://us-east.cdn.yourapp.com',
    'eu-west': 'https://eu-west.cdn.yourapp.com',
    'ap-south': 'https://ap-south.cdn.yourapp.com'
  }[userRegion] || 'https://cdn.yourapp.com';

  const files = [
    {
      url: `${cdnBaseUrl}/shared/core-library.zip`,
      zipPath: 'lib/core.zip'
    },
    // ... tenant-specific files
  ];

  const zipStream = await createZipStream(files);
  zipStream.body.pipe(res);
});

Benefit: Lower latency and egress costs by serving from regional CDNs

Monitoring and Analytics

// Track download patterns across tenants
async function trackDownload(tenantId, packageType, fileCount, estimatedSize) {
  await analytics.track({
    event: 'package_downloaded',
    properties: {
      tenantId: tenantId,
      packageType: packageType,
      fileCount: fileCount,
      estimatedSize: estimatedSize,
      timestamp: new Date()
    }
  });

  // Update tenant download quota
  await db.incrementTenantDownloads(tenantId);

  // Alert if tenant exceeds fair use
  const monthlyDownloads = await db.getTenantMonthlyDownloads(tenantId);
  if (monthlyDownloads > 1000) {
    await sendAlert('high_usage', {
      tenantId: tenantId,
      downloads: monthlyDownloads
    });
  }
}

app.get('/api/tenants/:tenantId/download', async (req, res) => {
  const files = await assembleTenantPackage(req.params.tenantId);

  // Estimate size before streaming
  const estimate = await fetch('https://zipstream.app/api/size-estimates', {
    method: 'POST',
    body: JSON.stringify({ files })
  });

  const { estimatedTotalSize } = await estimate.json();

  // Track download
  await trackDownload(
    req.params.tenantId,
    'full-package',
    files.length,
    estimatedTotalSize
  );

  // Create and stream package
  const zipStream = await createZipStream(files);
  zipStream.body.pipe(res);
});

Best Practices

1. Implement Download Quotas

// Limit downloads per tenant to prevent abuse
async function checkTenantQuota(tenantId) {
  const quota = await db.getTenantQuota(tenantId);
  const usage = await db.getTenantMonthlyDownloads(tenantId);

  if (usage >= quota.monthlyDownloads) {
    throw new Error('Monthly download quota exceeded');
  }

  return {
    remaining: quota.monthlyDownloads - usage,
    resetsAt: getNextMonthStart()
  };
}

app.get('/api/tenants/:tenantId/download', async (req, res) => {
  try {
    const quota = await checkTenantQuota(req.params.tenantId);

    // Include quota headers
    res.set('X-Download-Quota-Remaining', quota.remaining);
    res.set('X-Download-Quota-Reset', quota.resetsAt.toISOString());

    // Proceed with download
    const zipStream = await createTenantPackage(req.params.tenantId);
    zipStream.body.pipe(res);

  } catch (error) {
    res.status(429).json({
      error: error.message,
      quotaResetsAt: getNextMonthStart()
    });
  }
});

2. Cache Tenant Configurations

// Cache assembled file lists to avoid repeated DB queries
const tenantPackageCache = new NodeCache({ stdTTL: 300 }); // 5 min cache

async function getTenantPackageDescriptor(tenantId) {
  // Check cache first
  const cached = tenantPackageCache.get(tenantId);
  if (cached) return cached;

  // Assemble package
  const files = await assembleTenantPackage(tenantId);

  // Cache result
  tenantPackageCache.set(tenantId, files);

  return files;
}

3. Validate Tenant Access

// Ensure tenant has access to download
async function validateTenantAccess(tenantId, userId) {
  const tenant = await db.getTenant(tenantId);

  // Check if user belongs to tenant
  if (tenant.ownerId !== userId && !tenant.memberIds.includes(userId)) {
    throw new Error('Unauthorized access to tenant resources');
  }

  // Check if tenant is active
  if (tenant.status !== 'active') {
    throw new Error('Tenant account is inactive');
  }

  // Check if tenant subscription is valid
  if (tenant.subscriptionExpiry < new Date()) {
    throw new Error('Tenant subscription has expired');
  }

  return tenant;
}

4. Generate Audit Trails

// Log all package downloads for compliance
async function auditDownload(tenantId, userId, files, ipAddress) {
  await db.createAuditLog({
    action: 'package_download',
    tenantId: tenantId,
    userId: userId,
    metadata: {
      fileCount: files.length,
      fileNames: files.map(f => f.zipPath),
      ipAddress: ipAddress,
      userAgent: req.headers['user-agent']
    },
    timestamp: new Date()
  });
}

Cost Comparison: 10,000 Tenants

Component Pre-Generated ZipStream Savings
Storage (S3) $230/month $12/month $218
Regeneration compute $50/month $0 $50
Bandwidth (egress) ~$100/month ~$100/month $0
Development time High (maintenance) Low (set once)
Total $380/month $112/month $268/month (71%)

Performance Metrics

Based on production multi-tenant SaaS deployments:

Metric Value
Average package assembly time second
Time to first byte <500ms
Concurrent tenant downloads 100+ per server
Storage overhead per tenant 95% reduction
Update propagation time Instant

Scaling Considerations

Rate Limits

  • Default: 10 requests/hour per IP
  • Multi-tenant impact: Tenants may share IPs (corporate NAT)
  • Solution: Contact ZipStream for tenant-based rate limiting

File Count Limits (50 files)

Workaround: Pre-bundle related files into nested ZIPs

// Bundle small config files into one archive
const configBundle = await createNestedZip([
  'config.json',
  'settings.xml',
  'preferences.ini'
]);
files.push({ url: configBundle, zipPath: 'config.zip' });

Archive Size Limits (5GB)

Solution: Offer tiered packages (Basic, Pro, Enterprise)

if (tenant.tier === 'basic') {
  files = files.filter(f => f.essential); // Only essential files
}

Conclusion

ZipStream transforms multi-tenant SaaS delivery economics:

  • 80-95% storage cost reduction: Store shared resources once
  • Instant updates: No regeneration lag when core libraries change
  • Infinite combinations: Support any feature mix without pre-generation
  • Version flexibility: Serve multiple versions simultaneously
  • Better scalability: Linear storage growth becomes logarithmic

Whether you’re building a white-label platform, educational SaaS, or multi-tenant marketplace, ZipStream provides the infrastructure to deliver personalized content at scale without the storage overhead.


Ready to scale your multi-tenant delivery? Get started with ZipStream

Back to All Articles

Ready to get started?

Try ZipStream and start building scalable file delivery infrastructure.