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
Ready to get started?
Try ZipStream and start building scalable file delivery infrastructure.