Your cloud bill is hemorrhaging money on unnecessary services. A mid-sized SaaS with 50,000 users often pays AWS between $18,000 and $24,000 yearly for a setup including RDS PostgreSQL, S3 storage, Lambda functions, Cognito authentication, and API Gateway. Transitioning this same architecture to Supabase reduces your annual expenses to $3,600—or even less. That's $15,000 returned to your budget, and unlike many migration horror stories, this one simplifies your codebase.
Photo: Poddar Group of Institutions on Unsplash
The hidden truth of modern cloud infrastructure is that AWS sold you modularity as a feature when it's actually vendor lock-in masked as flexibility. Each service demands its own SDK, authentication flow, monitoring dashboard, and billing line item. Supabase consolidates PostgreSQL, authentication, storage, real-time subscriptions, and edge functions into one platform with a unified API. The cost difference goes beyond pricing—it's about the unnecessary architectural bloat you've been paying for without realizing it.
The Real Cost Breakdown: Where AWS Drains You
Let’s dissect a typical production stack for a B2B SaaS serving 50,000 monthly active users with moderate database operations, file uploads, and API traffic. On AWS, you’re running:
RDS PostgreSQL (db.t3.medium): $146/month for compute, plus $115/month for 500GB storage with provisioned IOPS because your queries lagged on standard storage. Automated backups add another $50/month. Total: $311/month or $3,732/year.
S3 + CloudFront: 2TB of user-uploaded assets, 500GB monthly transfer, CloudFront caching for global delivery. S3 storage costs $46/month, PUT/GET requests add $15/month, CloudFront transfer costs $85/month. Total: $146/month or $1,752/year.
Lambda + API Gateway: 10 million function invocations monthly (user webhooks, background jobs, image processing). Lambda compute costs $83/month at 512MB average memory, API Gateway adds $35/month for HTTP APIs. Total: $118/month or $1,416/year.
Cognito: User authentication for 50,000 MAUs. The first 50,000 users are free, but SMS-based MFA (which 18% of your users enabled for compliance) costs $0.05 per message. Monthly MFA costs: $450/month or $5,400/year.
CloudWatch + X-Ray: Basic monitoring and tracing because you need to know when things break. Logs ingestion for 50GB/month, metrics, and distributed tracing. Total: $67/month or $804/year.
VPC NAT Gateway: Because your Lambda functions need internet access to call third-party APIs. $45/month or $540/year.
Route53 + ACM: DNS hosting and SSL certificates. $12/month or $144/year.
AWS bill before optimization: $1,149/month minimum, realistically $1,800+/month when you factor in data transfer overages, unattached EBS volumes you forgot to delete, and that Elasticsearch cluster from a feature you deprecated six months ago. Annual: $21,600.
Now the Supabase equivalent. You're on the Pro plan at $25/month, which includes:
- 8GB PostgreSQL database with connection pooling (handles your 50K users easily)
- 100GB file storage with global CDN via Supabase Storage
- 50GB bandwidth (your actual usage)
- Authentication with unlimited users, social logins, and magic links (no SMS MFA because you switched to authenticator apps when you realized the cost savings)
- 2 million Edge Function invocations
- Real-time database subscriptions (a feature AWS charges extra for via AppSync)
- Automatic SSL, daily backups, point-in-time recovery
You'll surpass the 50GB bandwidth allowance—your actual usage is closer to 500GB monthly when accounting for API responses and file downloads. Supabase charges $0.09/GB for overages. That's 450GB × $0.09 = $40.50/month in bandwidth.
Your Edge Functions exceed 2M invocations. You're at 10M/month. Overage pricing: $2 per 1 million invocations. That's 8M × $2 = $16/month.
You need the database upgraded because 8GB isn't sufficient for your analytics tables. You move to the Team plan at $599/month instead, which offers a dedicated compute instance with 16GB RAM, 200GB storage, and 200GB bandwidth included. Now your overages drop to 300GB × $0.09 = $27/month plus the same $16/month for functions.
Supabase total: $599 + $27 + $16 = $642/month or $7,704/year.
Savings: $21,600 - $7,704 = $13,896/year. And that's the pessimistic calculation where you're on the Team tier. If you optimize your bandwidth (implement better caching, compress responses, use next/image optimization for static assets), you drop back to the Pro plan and save $17,304 annually.
What You Actually Migrate (And What You Don't)
Photo: Poddar Group of Institutions on Unsplash
Supabase’s strength is in its simplicity—it's essentially PostgreSQL with a REST API layer. Data migration boils down to a pg_dump and pg_restore operation, sparing you from complex ETL pipelines.
Database layer: Export your RDS schema and data using pg_dump, restore to Supabase. The only snag is RDS extensions—if you're using PostGIS, uuid-ossp, or pg_trgm, Supabase supports all. The one extension they don't support is some obscure Oracle compatibility layer you were using to appease a legacy integration. You rewrite that stored procedure in 40 lines of TypeScript and move on.
Authentication: This is where you save the most time. AWS Cognito has a steep learning curve—user pools, identity pools, federated identities, custom attributes stored in DynamoDB because Cognito limits metadata to 2KB. Supabase Auth is PostgreSQL-native. User metadata resides in auth.users, your application data references auth.uid(), and row-level security policies enforce access control at the database layer.
Migrating from Cognito involves exporting users (Cognito supports this via CSV or API), hashing passwords using bcrypt (Supabase compatible), and importing via the Supabase CLI. Social logins (Google, GitHub) work identically. SMS MFA gets replaced by TOTP (authenticator apps), which is more secure and costs you nothing.
File storage: S3 is the industry standard, but 90% of applications use it as a simple blob store. You're not running complex lifecycle policies or cross-region replication. Supabase Storage is S3-compatible (built on top of S3 or R2 depending on your deployment), with a simpler API.
Migration script in Node.js:
const AWS = require('aws-sdk');
const { createClient } = require('@supabase/supabase-js');
const s3 = new AWS.S3();
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
async function migrateFiles(bucket, prefix) {
const objects = await s3.listObjectsV2({
Bucket: bucket,
Prefix: prefix
}).promise();
for (const obj of objects.Contents) {
const file = await s3.getObject({
Bucket: bucket,
Key: obj.Key
}).promise();
await supabase.storage
.from('user-uploads')
.upload(obj.Key, file.Body, {
contentType: file.ContentType,
cacheControl: '3600'
});
}
}
Run this overnight, rsync-style with checkpointing, and your 2TB migrates in about 6 hours, depending on your bandwidth.
Edge Functions: Lambda functions become Supabase Edge Functions, which run on Deno Deploy at the edge. If you wrote Lambda in Node.js, you rewrite in Deno-compatible TypeScript. Most npm packages work fine; the exceptions are native bindings (sharp for image processing) which you replace with web-standard alternatives or keep as a separate Lambda if the cost is negligible.
The benefit here is reduced cold start latency. AWS Lambda in a VPC has 2-4 second cold starts because it has to attach an ENI. Supabase Edge Functions start in 50-150ms globally. Your webhook handlers go from frustratingly slow to instant.
The Architecture Changes That Actually Matter
Switching to Supabase isn't just a lift-and-shift—it forces you to embrace PostgreSQL-native patterns that eliminate entire layers of your stack.
Row-level security replaces API middleware: On AWS, you wrote Express middleware to check if req.user.id matches the resource owner. That's 200 lines of code per route, plus tests, plus the cognitive overhead of maintaining authorization logic separate from your data model.
Supabase uses PostgreSQL RLS policies:
CREATE POLICY "Users can only read their own data"
ON public.documents
FOR SELECT
USING (auth.uid() = user_id);
Your API layer shrinks from 15 endpoints to 3. The Supabase client handles the rest:
const { data, error } = await supabase
.from('documents')
.select('*')
.eq('user_id', user.id); // RLS enforces this automatically
You delete 3,000 lines of middleware. Your API Gateway bill drops to zero because you're calling Supabase's auto-generated REST API directly from the client.
Real-time subscriptions replace polling: You were polling your Lambda function every 30 seconds to check for new notifications. That's 2,880 requests per user per day, or 144 million Lambda invocations monthly for 50K users—except you weren't doing that because the cost would be insane, so your notifications were delayed by up to 5 minutes and users complained.
Supabase provides real-time Postgres subscriptions:
const subscription = supabase
.from('notifications')
.on('INSERT', payload => {
showNotification(payload.new);
})
.subscribe();
WebSocket connection stays open, database NOTIFY/LISTEN handles pub/sub, users get instant updates. You didn’t have to write a WebSocket server or deploy Socket.io on ECS. It just works.
Connection pooling fixes the serverless problem: Lambda + RDS was always a problematic combination because every function invocation opens a new database connection. You worked around this with RDS Proxy ($0.015/hour per vCPU, $262/month minimum) or connection pooling libraries that leaked connections under load.
Supabase uses PgBouncer in transaction mode by default. Your Edge Functions share a pool of 15 persistent connections. You stop worrying about too many clients errors.
The Migration Nobody Tells You About: Developer Experience
The hidden cost of AWS is context switching. Your team is spread across 12 different consoles—RDS for databases, S3 for files, Cognito for users, CloudWatch for logs, IAM for permissions. Every service has its own mental model, API design philosophy, and documentation style.
Supabase consolidates this into a single dashboard. Database schema, authentication users, storage buckets, Edge Function logs, and API usage all live in one place. Your junior developers stop asking "where do I find X?" because there's only one place to look.
The SQL editor is PostgreSQL-native with autocomplete, explain plans, and saved queries. You stop SSHing into a bastion host to run psql commands. Your table editor visualizes foreign keys as actual relationships instead of making you mentally map user_id to users.id across 20 tables.
TypeScript types generate automatically from your schema. Install the CLI, run supabase gen types typescript, and you get:
export interface Database {
public: {
Tables: {
documents: {
Row: {
id: string
user_id: string
title: string
created_at: string
}
Insert: {
id?: string
user_id: string
title: string
created_at?: string
}
Update: {
id?: string
user_id?: string
title?: string
created_at?: string
}
}
}
}
}
Your client calls are now fully typed. You delete react-query boilerplate and Zod validation schemas because the types already enforce the contract.
When Supabase Actually Costs More (And You Should Stay on AWS)
This isn't a universal recommendation. There are legitimate reasons to stay on AWS or hybrid architectures that make sense.
You're already at scale with reserved instances: If you've committed to 3-year RDS reserved instances and optimized your S3 storage tiers, your AWS bill might already be $600/month. The migration effort isn't worth 6 months of savings.
You need multi-region active-active: Supabase runs in a single region (you choose at project creation). If you need sub-50ms latency globally with active-active writes, you're running Aurora Global Database or CockroachDB. Supabase replication is read-only replicas, not distributed writes.
Compliance requires AWS-specific certifications: Some enterprises mandate FedRAMP or AWS-specific SOC 2 attestations. Supabase has SOC 2 Type II and HIPAA compliance, but if your contract explicitly says "must run on AWS GovCloud," you don't have a choice.
You're using AWS-specific services deeply: If your architecture depends on SQS, SNS, Step Functions, EventBridge, or DynamoDB streams, you're not migrating off AWS. Supabase doesn't replace those. You might hybrid—database and auth on Supabase, event-driven workflows still on AWS.
Your database is genuinely huge: Supabase's largest plan is 400GB RAM, 1TB storage. If you're running a multi-terabyte analytics database on RDS r6g.16xlarge, you're not migrating. You're probably on Aurora Serverless v2 with auto-scaling up to 128 ACUs anyway.
Conclusion: The Real Value Is Simplicity, Not Just Price
The $15,000 annual savings is the headline, but the actual win is eliminating complexity. You're consolidating 7 AWS services into 1 platform, reducing your operational surface area by 80%, and giving your team a development experience that doesn’t require a PhD in AWS IAM to understand.
The best migrations happen when a startup hits product-market fit and realizes their infrastructure spend is growing faster than revenue. You're paying AWS $1,800/month while doing $12K MRR, and that ratio feels wrong. Supabase brings your infrastructure costs back in line with your stage—you're pre-Series A, not Google. Act like it.
Have you run a similar migration, or are you stuck on AWS because of a specific service you can't replace? What's actually keeping you from consolidating your stack?