Why We Moved from Kubernetes to AWS Lambda
In April 2026, we deleted our entire Kubernetes cluster. Not because it was broken — it worked perfectly. We deleted it because it was bankrupting us.
This is the story of how Delt migrated from Google Kubernetes Engine (GKE Standard) to AWS Lambda, reducing infrastructure costs by 99% and improving cold start times by 60×.
The Numbers
| Metric | GKE Standard (Before) | AWS Lambda (After) | Improvement |
|---|---|---|---|
| Infrastructure cost | $139/mo minimum | $2.34/mo per active tenant | 98.3% reduction |
| Cold start | 30-60 seconds | 500ms | 60× faster |
| Idle cost | $139/mo (cluster always running) | $0 | ∞ improvement |
| Components to manage | 11 (ingress, cert-manager, etc.) | 3 (Lambda, SQS, S3) | 73% fewer |
| Deploy time | 60 seconds | 60 seconds | Same |
The Problem: Kubernetes Economics Don't Work for Early-Stage PaaS
Delt v1 ran on GKE Standard in Jakarta (asia-southeast2-a). The architecture was solid:
- Namespace-per-tenant isolation
- cert-manager for automatic SSL
- ingress-nginx for traffic routing
- Valkey for cache/queues
- SeaweedFS for object storage
- Google Managed Prometheus for metrics
The problem: All of this infrastructure cost $139/month minimum — even with zero paying customers.
Cost Breakdown (GKE Standard)
- GKE cluster management fee: $73/mo
- System node (e2-standard-2): $66/mo
- Paid/Hobby nodes: $0 (autoscale min=0, but cluster fee is fixed)
Total minimum burn: $139/mo = Rp 2.24M/month
With zero revenue and limited GCP credits, we had ~18 days of runway. The math was simple: either get 5 paying customers immediately or find a cheaper architecture.
The Options We Evaluated
Option 1: Stay on GKE, Optimize
- Downsize nodes further → already at minimum
- Share cluster with other projects → defeats tenant isolation
- Verdict: Can't optimize below the $73/mo cluster fee
Option 2: ECS Fargate (AWS Containers)
- No cluster fee (pay per task)
- But: 30-60 second cold starts for scale-to-zero
- Minimum cost: ~$15-30/mo per active tenant
- Verdict: Cold starts kill the developer experience
Option 3: AWS Lambda + Bref
- True scale-to-zero ($0 when idle)
- 500ms cold start (Bref PHP runtime is optimized)
- Pay per invocation (~$0.17/mo for a hobby project)
- Verdict: Winner — best economics AND best cold start
Why Lambda + Bref Works for PHP
The conventional wisdom says "PHP doesn't work on Lambda." That was true in 2020. In 2026, Bref has processed 40+ billion Lambda invocations globally. It's production-ready.
What Bref Provides
- Custom Lambda runtime for PHP 8.3/8.4
- FPM layer that handles Laravel's request lifecycle
- Composer integration for dependency management
- Laravel-specific optimizations (config caching, route caching)
Cold Start Reality
- Average cold start: 500ms (acceptable for web apps)
- Warm invocation: 5-15ms (faster than any container)
- Provisioned concurrency: 0ms cold start (Business plan feature)
The 500ms cold start happens only on the first request after idle. Subsequent requests are served in milliseconds. For comparison, GKE pods took 30-60 seconds to start from zero replicas.
The Migration: What We Replaced
Infrastructure Components Eliminated
| GKE Component | Lambda Replacement | Complexity Saved |
|---|---|---|
| ingress-nginx | API Gateway | Managed service, zero config |
| cert-manager + Let's Encrypt | Cloudflare SSL | Automatic, no renewal |
| Valkey (Redis) | ElastiCache or DynamoDB | Managed, auto-scale |
| SeaweedFS | S3 | Native AWS, infinite scale |
| Google Managed Prometheus | CloudWatch | Built-in metrics |
| pg_cron (scheduler) | EventBridge | Serverless cron |
| Supervisor (queue workers) | SQS + Lambda | Auto-scaling workers |
| Kubernetes Deployments | Lambda functions | No replica management |
| Kubernetes Services | API Gateway routes | Automatic routing |
| ResourceQuota/LimitRange | Lambda memory config | Single setting |
| Namespace isolation | IAM + separate functions | AWS-native isolation |
From 11 infrastructure components to 3 core services: Lambda, SQS, S3.
What Stayed the Same
- Supabase — still the single source of truth for all tenant state
- Git-push deployment — still 60 seconds from push to live
- Tenant isolation — now via IAM policies instead of K8s namespaces
- Environment variable encryption — still Supabase Vault
The New Architecture
GitHub/GitLab/Bitbucket Push
│
▼
[Webhook → Edge Function]
│ → triggers CodeBuild
│ → writes deployments.status = QUEUED
▼
[AWS CodeBuild]
│ → builds Bref deployment package
│ → uploads to S3
▼
[Deploy Edge Function]
│ → updates Lambda function code
│ → configures API Gateway
│ → provisions SQS queue (if Growth+)
│ → sets up EventBridge schedule (if Growth+)
▼
[App is LIVE — ~60 seconds from push]
Cost Per Tenant (Lambda Model)
Hobby Tier (Free)
- Lambda: ~$0.17/mo (10K requests/day, 500ms avg duration, 512MB)
- S3: ~$0.02/mo (1GB storage)
- API Gateway: ~$0.04/mo
- Total: ~$0.23/mo per active Hobby tenant
- Idle cost: $0 (Lambda charges nothing when not invoked)
Growth Tier ($29/mo)
- Lambda: ~$0.85/mo (50K requests/day, 300ms avg, 1GB)
- SQS: ~$0.40/mo (queue processing)
- RDS share: ~$1.00/mo (shared PostgreSQL)
- S3: ~$0.05/mo
- EventBridge: ~$0.04/mo
- Total: ~$2.34/mo per Growth tenant
- Gross margin: 92% ($29 revenue - $2.34 cost)
Business Tier ($59/mo)
- Lambda + provisioned concurrency: ~$4.50/mo
- SQS: ~$0.80/mo
- RDS share: ~$2.00/mo
- S3: ~$0.10/mo
- Total: ~$7.40/mo per Business tenant
- Gross margin: 87% ($59 revenue - $7.40 cost)
Compare this to GKE where the minimum cost was $139/mo regardless of tenant count.
Challenges We Faced
1. PHP Session Handling
Lambda is stateless — no persistent filesystem between invocations. Solution: moved sessions to database/cache (standard Laravel practice anyway).
2. File Uploads
Lambda has a 6MB request body limit and no persistent disk. Solution: direct-to-S3 uploads with presigned URLs. Laravel's filesystem abstraction makes this transparent.
3. Long-Running Processes
Lambda has a 15-minute timeout. Solution: queue workers via SQS (each job is a separate Lambda invocation). No more Supervisor managing worker processes.
4. Scheduled Tasks
No cron on Lambda. Solution: EventBridge triggers php artisan schedule:run at configured intervals. More reliable than cron (no missed executions from server restarts).
5. WebSockets
Lambda doesn't support persistent connections. Solution: API Gateway WebSocket API for real-time features (deployment status updates). Most Laravel apps don't need WebSockets anyway.
What We Learned
1. Kubernetes is Overkill for Early-Stage Products
K8s is incredible for large-scale operations. But for a startup with 0-10 tenants, the operational overhead and minimum cost are unjustifiable. Start serverless, migrate to containers when you need the control.
2. Cold Starts Are Overblown
500ms on first request is imperceptible to users. The internet has trained us to accept 1-3 second page loads. A 500ms cold start is faster than most API calls to third-party services.
3. Serverless Forces Better Architecture
Lambda's constraints (stateless, time-limited, pay-per-use) force you to build better applications: proper queue usage, efficient database queries, minimal boot time. These are good practices regardless of hosting.
4. The Best Infrastructure is the One You Don't Manage
Every component we eliminated (ingress-nginx, cert-manager, Valkey, SeaweedFS, Prometheus) was a potential failure point. Fewer components = fewer 3 AM pages.
Should You Migrate from Kubernetes to Lambda?
Yes, if:
- Your per-tenant economics don't justify cluster overhead
- You need true scale-to-zero for cost efficiency
- Your workloads are request-driven (web apps, APIs)
- You want to eliminate infrastructure management
No, if:
- You need persistent connections (WebSocket-heavy apps)
- Your workloads are compute-intensive and long-running (>15 min)
- You need GPU access
- You're already at scale where K8s economics work (100+ pods)
Conclusion
Moving from Kubernetes to Lambda wasn't a downgrade — it was an upgrade in every dimension that matters for an early-stage PaaS:
- 99% cost reduction ($139/mo → $2.34/mo per tenant)
- 60× faster cold starts (30-60s → 500ms)
- 73% fewer components to manage and monitor
- Same deploy experience (60 seconds, git push → live)
The best part? Our customers notice zero difference. Same 60-second deploys, same managed experience. They just don't know (or care) that Lambda is running underneath instead of Kubernetes.
Experience serverless Laravel hosting — Deploy your first app free →