AWS Builder Center
From Vibe to Production: A Startup's Guide to Graduating from Supabase to AWS

From Vibe to Production: A Startup's Guide to Graduating from Supabase to AWS

Executive summary: Supabase is the right database for your MVP. You shouldn't move until a specific trigger fires - and most startups trigger one within 12-18 months of their first enterprise customer. When it does, this guide gives you a two-step migration path that keeps your codebase intact, gets you inside a VPC in days, and positions Aurora serverless as the production target when you're ready to fully graduate.

Senior Startups Solutions Architect at AWS

You Don't Need to Move Yet

If you're pre-Series A, building fast, and haven't yet signed an enterprise customer, this guide isn't urgent for you. A database migration before you need one is wasted engineering time. Revisit this when one of the triggers below applies.
Supabase is a solid choice for an MVP. Zero-config PostgreSQL, bundled auth, storage, and real-time subscriptions; it removes a lot of setup overhead and gets you to a working backend quickly. In 2026, the Lovable or Cursor + Supabase + Cloudflare Workers stack is a legitimate pattern for early-stage products, and many startups use it well into their growth phase.
Stay on it until one of the triggers below fires. Database migrations are expensive and risky.

The Graduation Triggers - When Supabase Stops Being Enough

These are the five moments when the conversation shifts from "Supabase is fine" to "we need to move." You'll recognise them when they happen.

Trigger 1: An Enterprise Customer Asks for Your SLA

This is the most common trigger and it usually happens fast; in a sales call, not a technical review.
The reality: Supabase's 99.9% uptime SLA is only available on the Enterprise plan, which starts at roughly $25,000/year with an annual contract. If you're on Free, Pro ($25/month), or Team ($599/month), you have no contractual SLA. None. (Supabase pricing )
What it looks like in practice: You're in a due diligence call with a Series B fintech or a healthcare company. Their vendor risk team asks "what's your database SLA?" You go quiet, or you say "Supabase has 99.9% uptime" and they ask you to send the SLA document. There isn't one for your plan.
The AWS answer: Amazon RDS Multi-AZ has a 99.95% uptime SLA. Aurora serverless has a 99.99% SLA. These are contractual and carry service credits. (Amazon RDS SLA ) (Amazon Aurora SLA )

Trigger 2: A CISO Review Flags Your Public Database Endpoint

Supabase databases are publicly reachable by default. That's a feature, not a bug; it's what makes the developer experience so fast. But regulated industries have a hard requirement: production databases must not have a public endpoint.
The reality: Supabase launched AWS VPC Lattice private networking in Q1 2026, which allows private connectivity to AWS resources without public internet exposure. (Supabase changelog, February 2026 ) This is a genuine improvement. But it requires configuration, and it's not the default. If you haven't set it up, your database is publicly accessible.
What it looks like in practice: Your first enterprise customer (or their CISO) runs a vendor security questionnaire. Question 47: "Does your production database have a public endpoint?" If you're on standard Supabase, the honest answer is yes.
The second issue: SOC2 Type II requires IAM-based access control; not just database username/password credentials. AWS IAM integration with RDS/Aurora lets you authenticate using IAM roles and policies, which maps cleanly to the SOC2 access control requirements. Supabase doesn't offer this.
The AWS answer: Your database inside a private subnet in a VPC, with no public endpoint, with IAM authentication; this is the default AWS setup, not a premium feature.

Trigger 3: Your Legal Team Gets a GDPR Request They Can't Answer

The reality: GDPR data subject access requests (DSARs) require you to produce a complete record of what personal data you hold on an individual, where it lives, and who has accessed it. This requires per-table audit logs and a clear data residency story.
Supabase does not provide per-table audit logs on free, pro, or team plans. Customer-managed encryption keys (BYOK) are an Enterprise feature. And "data residency" on Supabase means choosing a region when you create a project - there's no enforcement mechanism, no legal agreement at the infrastructure level, and no way to prove to a regulator that your data never left eu-central-1.
What it looks like in practice: Your legal team receives a GDPR DSAR from a user in Germany asking for all data you hold on them and a log of every system that has accessed it. Your database has the data. You have no audit log. You have no formal data processing agreement with Supabase that specifies data residency guarantees at the infrastructure level for your plan tier.
The SOC2 dimension: SOC2 Type II requires 6–12 months of demonstrable controls, including access logs, change management evidence, and encryption key management. None of these are turnkey on Supabase's lower tiers.
The AWS answer: CloudTrail for API-level audit logs, CloudWatch for database-level logs, AWS KMS with customer-managed keys for BYOK encryption, and AWS data processing agreements that come with formal data residency guarantees. These are standard, not add-ons.

Trigger 4: Your p99 Latency Spikes and You Have No Lever to Pull

The reality: Supabase Pro and Team plans run on shared infrastructure. "Shared tenancy" means your database performance is partially determined by what your neighbours on the same host are doing.
What it looks like in practice: You've just had a successful product launch or a press mention. Traffic spikes. Your p99 query latency goes from 20ms to 800ms. You open the Supabase dashboard. There's no "scale up" button that works immediately, no read replica you can point your read-heavy queries at, and no way to isolate the noisy neighbour causing the shared host CPU to spike.
The connection limit problem: At 60 connections on Pro, a modest Node.js application with multiple concurrent workers can exhaust your connection pool. Supavisor handles connection pooling but introduces its own latency layer and, on shared infrastructure, is subject to the same capacity constraints as everything else.
The AWS answer: Aurora serverless scales ACUs (Aurora Capacity Units) automatically in response to load. RDS Proxy provides managed connection pooling with no latency overhead for cached connections. Read replicas are a one-click addition. You have levers.

Trigger 5: An Enterprise Customer Asks for a Dedicated Instance

The reality: Multi-tenant SaaS products on Supabase typically use row-level security (RLS) to isolate customer data within a shared database. Supabase supports RLS well and it works. But some enterprise customers - particularly in FSI, healthcare, and government - require complete data isolation: their data in a separate database, not just separate rows.
At scale, database-per-tenant on Supabase means paying per project, managing per-project connection strings, and dealing with Supabase's project limits. This gets expensive and operationally complex fast.
What it looks like in practice: A large financial services company wants to buy your SaaS product but requires that their data be stored in a dedicated database instance, not shared with other customers. They'll want to see this in your SOC2 report and may ask for penetration test evidence. Supabase's architecture makes this a significant engineering effort.
The AWS answer: Aurora's database-per-tenant model is well-understood. Atlassian runs 4 million individual Jira PostgreSQL databases on Aurora — each Jira tenant gets their own database, with 50% cost reduction vs their previous setup. (Atlassian Engineering Blog , AWS Case Study )

The Migration Path - Two Steps, Not One

The most common mistake is treating this as a big-bang migration: take everything off Supabase and rebuild it on AWS in a single sprint. That's how you create a month-long incident.
The right path is two steps, separated by time. Step 1 can be done in 1–2 weeks. Step 2 can be done months later when you're ready. And critically, between Step 1 and Step 2, your application code barely changes.

Step 1: Self-Hosted Supabase on AWS ECS

This is the move that gets you inside a VPC and gives you full control over your network security - without rewriting a line of application code.
What you’re doing: Taking the Supabase stack (which is entirely open source) and running it yourself on AWS infrastructure inside your own VPC. The stateless services (Kong, GoTrue, PostgREST, Realtime, Storage API) run on ECS Fargate. The database runs as a container using Supabase’s own PostgreSQL image (supabase/postgres) on ECS or EC2.
This means Step 1 gives you VPC isolation and network-level security controls; but you are still managing the database container yourself, without the managed patching, Multi-AZ failover, and RDS SLA that come in Step 2.
Why your code doesn’t change: Supabase’s value is its API layer: the PostgREST auto-generated REST API, the GoTrue auth service, the Realtime WebSocket server, the Storage API. When you self-host, these same services run in your AWS environment. Your frontend still calls supabase.from('users').select(). Your auth tokens still work. Your storage buckets still respond to the same API. The only thing that changes is where the infrastructure runs.

Architecture

See the diagram in the Architecture Diagrams section.
ECS Fargate for the stateless services: Kong, GoTrue, PostgREST, Realtime, and the Storage API are all stateless. They run cleanly as ECS Fargate tasks; no servers to manage, auto-scaling, pay only for what you use. The supabase/supabase  repository includes a Docker Compose reference that maps directly to ECS task definitions.
The database - ECS or EC2: Run the supabase/postgres Docker image as an ECS task with persistent EFS storage, or on a dedicated EC2 instance with EBS. Place it in a private subnet with no public endpoint. You manage patching and backups yourself; this is the trade-off vs RDS. AWS Backup can handle EBS/EFS snapshots. For Multi-AZ, you will need to configure PostgreSQL streaming replication manually, or accept that fully managed HA arrives in Step 2 when you move to Aurora.
Secrets Manager: All credentials (database password, JWT secret, service role key, SMTP credentials) move from environment variables to AWS Secrets Manager. ECS tasks retrieve them at runtime. This is a SOC2 requirement.
Application Load Balancer: Routes traffic to Kong. Configure WAF rules here for additional security controls that SOC2 auditors will want to see.

What you get from Step 1

RequirementBefore (Supabase SaaS)After (Self-hosted on ECS)
Database public endpoint✗ Public by default✓ Private subnet, no public endpoint
Managed SLANone (Pro/Team)✗ Self-managed (HA requires manual setup)
Audit logsLimited✓ CloudTrail + CloudWatch full logs
BYOK encryptionEnterprise only✓ AWS KMS customer-managed keys
IAM access control✗ DB credentials only✓ IAM roles + policies (network layer)
GDPR data residencyRegion selection only✓ AWS DPA + region enforcement
SOC2 compatibilityDifficult✓ Standard AWS controls
Application code changes—Zero
> The honest trade-off: Step 1 gives you VPC isolation, audit logs, KMS encryption, and SOC2-compatible controls; but you’re still managing the database container yourself. A managed SLA and automated failover come in Step 2 when you move the database to Aurora.
Estimated migration time: 1–2 weeks for a team of 2–3 engineers with AWS experience. The database migration (using pg_dump / pg_restore) is the riskiest step; budget time for a dry run in staging.

Step 2: Aurora Serverless

You don't need to take Step 2 immediately after Step 1. Run self-hosted Supabase on ECS for a few months. Get comfortable with the operations. Then, when one of these conditions is true, take Step 2:
  • You need multi-region (Aurora Global Database for disaster recovery or latency reduction)
  • You need analytics (Aurora Zero-ETL to Amazon Redshift eliminates your ETL pipeline)
  • You need horizontal scale (Aurora Limitless for PostgreSQL, currently in preview)
  • You want to reduce operational overhead - replace GoTrue with Cognito, replace the Storage API with S3 + CloudFront, replace Realtime with AppSync or EventBridge
  • You need pgvector at scale for AI/ML features

What Aurora Serverless gives you that RDS doesn't

Auto-scaling compute with no instance sizing: Aurora serverless scales in Aurora Capacity Units (ACUs). You set a minimum and maximum; Aurora handles the rest. No "I need to upgrade to r7g.2xlarge" decisions. No downtime for vertical scaling.
Scale to zero for non-production: Dev and staging environments scale down to zero ACUs when idle. You pay nothing for a database that's not being queried. For a startup with 5 environments, this adds up.
Express configuration: A new Aurora PostgreSQL cluster is provisioned and ready to accept queries in seconds, using the --express-configuration flag or two clicks in the console. (AWS What's New, March 2026 ) This isn't the same as cold start time (which is ~15s for a scaled-to-zero cluster resuming) - it's the time to create a new cluster from scratch.
Zero-ETL to Redshift: If you add a BI or analytics requirement, Aurora's Zero-ETL integration with Amazon Redshift replicates your transactional data to Redshift continuously, with no DMS pipeline to manage. This directly eliminates the need for a separate ETL tool.
pgvector: Aurora PostgreSQL supports pgvector natively. For workloads under ~10M vectors (the vast majority of production RAG applications), this means you can run OLTP and semantic search on the same database; no separate vector database cluster to operate.

Migration from self-hosted Supabase to Aurora

Because your database is already standard PostgreSQL (the supabase/postgres container from Step 1), migrating to Aurora is a straightforward PostgreSQL-to-Aurora cutover. This is also the point where you drop the custom Supabase image entirely and hand database management to AWS.
1
2
3
4
5
6
7
8
9
# Option 1: pg_dump / pg_restore
pg_dump -h your-supabase-container-endpoint -U postgres -d your_db \> backup.sql
psql -h your-aurora-endpoint -U postgres -d your_db \< backup.sql

# Option 2: AWS DMS (for near-zero-downtime)
# Create a DMS replication instance
# Source endpoint: your supabase/postgres container (PostgreSQL source)
# Target endpoint: Aurora PostgreSQL
# Run full-load + CDC task, switch traffic when replication lag is \<1s
For the Supabase application services, the graduation path is:
Supabase serviceAWS native replacementMigration complexity
GoTrue (auth)Amazon CognitoMedium - token format changes, client SDK swap
PostgREST (REST API)Keep running on ECS, or replace with API Gateway + LambdaLow (keep) / High (replace)
Storage APIAmazon S3 + CloudFrontLow - presigned URLs replace Supabase storage URLs
Realtime (WebSockets)AWS AppSync (GraphQL subscriptions) or API Gateway WebSocketsMedium
Supavisor (connection pooling)RDS ProxyLow - update connection string
The most pragmatic approach for most startups: keep PostgREST running as an ECS service, migrate Storage to S3, and replace Supavisor with RDS Proxy. Replace GoTrue with Cognito when you have a dedicated sprint for it - auth migrations require careful session management.

Comparing Options: Supabase Enterprise vs AWS

If you've reached the point where you're evaluating whether to stay on Supabase Enterprise or move to AWS, here's a factual comparison to help you decide. Both are legitimate options depending on your priorities.
Supabase SLA and availability:
Supabase offers a 99.9% uptime SLA on the Enterprise plan. (Supabase pricing ) Free, Pro, and Team plans don't include a contractual SLA.
Pricing at scale:
  • Pro: $25/month per project - well-suited for a small number of projects
  • Team: $599/month per project
  • Enterprise: custom pricing with annual contract
At larger project counts, a dedicated AWS setup can be cost-competitive with Supabase Team or Enterprise. The right comparison depends on your specific usage - compute, storage, egress, and number of projects all factor in.
When Supabase Enterprise makes sense:
If you want to keep the Supabase developer experience (dashboard, branching, zero-config auth and storage) and have the budget for Enterprise, that's a valid choice. Supabase Enterprise includes dedicated infrastructure, a contractual SLA, and enterprise support. It's a supported, production-grade option.
When AWS makes more sense:
If your requirements include customer-managed encryption keys, fine-grained IAM policies, direct access to the underlying infrastructure, multi-region Active-Active, or Zero-ETL analytics pipelines - these are native to AWS and require more configuration on Supabase. Aurora also gives you the ability to scale to workloads like Atlassian's (4 million databases) that go beyond what a managed PaaS is designed for.

Decision Framework

SituationRecommendation
Pre-PMF, solo founder, no enterprise customersStay on Supabase Free/Pro - it's the right tool
Small team, <$1M ARR, no compliance requirementsStay on Supabase Pro/Team - optimise later
First enterprise customer asking for SLA or security questionnaireStep 1: Self-hosted Supabase on ECS (1–2 weeks)
SOC2 Type II audit startingStep 1: Self-hosted Supabase on ECS (required for audit controls)
GDPR DSAR you can't answer, or data residency requirementStep 1: Self-hosted Supabase on ECS
p99 latency issues under load, or hitting connection limitsStep 2: Aurora serverless (dedicated compute, RDS Proxy)
First multi-region requirement or DR strategy neededStep 2: Aurora serverless + Global Database
Adding analytics or BI to your productStep 2: Aurora + Zero-ETL to Redshift
AI/ML features, semantic search, RAG on your roadmapStep 2: Aurora serverless + pgvector
Enterprise customer requiring dedicated database instanceStep 2: Aurora serverless (database-per-tenant pattern)

Architecture Diagrams

Step 1: Self-Hosted Supabase on AWS ECS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Internet
│
▼
┌───────────────────────────────────────────────────────────┐
│ AWS Account │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ VPC (eu-west-1 or eu-central-1) │ │
│ │ │ │
│ │ Public Subnet │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ Application Load │ ← WAF Rules │ │
│ │ │ Balancer (HTTPS) │ │ │
│ │ └──────────┬──────────┘ │ │
│ │ │ │ │
│ │ Private Subnet (ECS Tasks) │ │
│ │ ┌──────────▼──────────────────────────────────┐ │ │
│ │ │ ECS Fargate Cluster │ │ │
│ │ │ ┌─────────┐ ┌─────────┐ ┌──────────────┐ │ │ │
│ │ │ │ Kong │ │ GoTrue │ │ PostgREST │ │ │ │
│ │ │ │(Gateway)│ │ (Auth) │ │ (REST API) │ │ │ │
│ │ │ └────┬────┘ └────┬────┘ └──────┬───────┘ │ │ │
│ │ │ │ │ │ │ │ │
│ │ │ ┌────┴───────────┴─────────────┴───────┐ │ │ │
│ │ │ │ Realtime (WebSocket) │ │ │ │
│ │ │ │ Storage API → S3 │ │ │ │
│ │ │ └──────────────────────────────────────┘ │ │ │
│ │ └───────────────────┬─────────────────────────┘ │ │
│ │ │ │ │
│ │ Private Subnet (Database) │ │
│ │ ┌───────────────────▼─────────────────────────┐ │ │
│ │ │ supabase/postgres container (ECS or EC2) │ │ │
│ │ │ - Private subnet, no public endpoint │ │ │
│ │ │ - pgsodium, pg_graphql, pg_net bundled │ │ │
│ │ │ - EFS (ECS) or EBS (EC2) for persistence │ │ │
│ │ │ - KMS encryption at rest │ │ │
│ │ │ - Manual backups via AWS Backup │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────┐ ┌──────────────────────────┐ │ │
│ │ │ Secrets Manager│ │ CloudTrail + CloudWatch │ │ │
│ │ │ (credentials) │ │ (audit logs) │ │ │
│ │ └────────────────┘ └──────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────┘

Step 2: Full Aurora Serverless Stack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Internet
│
▼
┌───────────────────────────────────────────────────────────┐
│ AWS Account │
│ │
│ CloudFront (global CDN for static assets + storage) │
│ │ │
│ ALB → API Gateway → Lambda / ECS (your app) │
│ │ │
│ ┌───────────────────────▼──────────────────────────────┐ │
│ │ Private Subnet │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ Aurora serverless (PostgreSQL-compatible) │ │ │
│ │ │ - Auto-scales ACUs 0.5 → 128 │ │ │
│ │ │ - Scale to zero for dev/staging │ │ │
│ │ │ - Zero-ETL → Redshift (analytics) │ │ │
│ │ │ - Global Database (multi-region DR) │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ RDS Proxy (connection pooling, replaces Supavisor) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ Cognito (auth, replaces GoTrue) │
│ S3 + CloudFront (storage, replaces Supabase Storage) │
│ AppSync / API Gateway WebSockets (replaces Realtime) │
│ Redshift Serverless (analytics via Zero-ETL) │
└───────────────────────────────────────────────────────────┘

ECS Task Definition - Self-Hosted Supabase (Reference)

The Supabase Docker Compose reference at github.com/supabase/supabase maps to ECS task definitions as follows. This is a starting point, not a production-ready configuration; you'll need to adjust resource limits, environment variables, and networking for your specific setup.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
"family": "supabase-postgrest",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::ACCOUNT:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::ACCOUNT:role/supabaseTaskRole",
"containerDefinitions": [
{
"name": "postgrest",
"image": "postgrest/postgrest:v12.0.2",
"portMappings": [{ "containerPort": 3000 }],
"environment": [],
"secrets": [
{
"name": "PGRST_DB_URI",
"valueFrom": "arn:aws:secretsmanager:eu-west-1:ACCOUNT:secret:supabase/db-uri"
},
{
"name": "PGRST_JWT_SECRET",
"valueFrom": "arn:aws:secretsmanager:eu-west-1:ACCOUNT:secret:supabase/jwt-secret"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/supabase/postgrest",
"awslogs-region": "eu-west-1",
"awslogs-stream-prefix": "postgrest"
}
}
}
]
}
Repeat this pattern for Kong (port 8000/8443), GoTrue (port 9999), Realtime (port 4000), and the Storage API (port 5000). All services retrieve credentials from Secrets Manager via the secrets field; never hardcode credentials in task definitions.

Common Mistakes and How to Avoid Them

Mistake 1: Running the database in a public subnet
Always deploy RDS/Aurora in a private subnet with PubliclyAccessible: false. The database should have no public endpoint - period. For administrative access (running queries, checking logs), connect via one of these patterns instead:
  • AWS Systems Manager Session Manager - port-forward from your laptop through SSM to the database without opening any inbound ports. No bastion host required.
  • Client VPN - connect your laptop to the VPC via VPN, then connect to the database as if it were on a local network.
  • Bastion host - a small EC2 instance in a public subnet that you SSH into, then connect to the database from there. The bastion has a public IP; the database does not.
Mistake 2: Copying the Supabase postgres superuser into production
Supabase's default PostgreSQL setup gives the application the postgres superuser role. In production, create a dedicated application role with only the permissions it needs. The principle of least privilege is a SOC2 requirement.
Mistake 3: Forgetting the pg_graphql and pgsodium extensions.
Supabase includes several PostgreSQL extensions that aren't installed by default on RDS. Run SELECT * FROM pg_extension; on your Supabase instance before migrating and ensure all extensions are available on your RDS instance. Most are, but pgsodium (Supabase's encryption extension) is not available on RDS and will need an alternative approach.
Mistake 4: Migrating auth before everything else
Auth is the hardest thing to migrate. Leave GoTrue running on ECS until everything else is stable. Users' existing sessions continue to work. Migrate to Cognito in a dedicated sprint with a carefully planned token cutover.
Mistake 5: No dry run.
Always do a full migration rehearsal in staging with a production-sized dataset before touching production. The migration itself is simple; the edge cases (sequences out of sync, extension mismatches, client connection limits during cutover) are where surprises hide.

Resources and Next Steps

Self-hosted Supabase:
Aurora serverless:
Migration assistance:
Proof points:
Get help:
Reach out to your AWS Solutions Architect for a free architecture review. We can walk through your specific Supabase setup, identify which graduation trigger applies to you, and produce a tailored migration plan.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article