ADR-0004: Hosting Topology and Platform Selection
Status
Accepted - 2025-10-24
Context
TVL Platform requires hosting for:
- Frontend: React web app (static assets + SSR)
- Backend: Node.js API server
- Workers: Background job processors
- Database: PostgreSQL
- Cache/Queue: Redis
- Observability: Metrics, logs, traces
Requirements:
- Fast time-to-market (< 3 months)
- Low operational overhead (small team)
- Cost-effective for MVP (< $200/mo)
- Scalable to 10,000+ users
- 99.9% uptime target
Decision
Multi-platform managed services architecture:
| Component | Platform | Rationale | 
|---|---|---|
| Frontend | Vercel | Best DX, global CDN, auto-scaling | 
| Backend API | Railway or Fly.io | Simple Docker deployment, auto-scaling | 
| Database | Supabase | Managed Postgres with RLS, Auth built-in | 
| Cache/Queue | Upstash Redis | Serverless Redis, pay-per-request pricing | 
| Observability | Grafana Cloud + Sentry | Free tier sufficient for MVP | 
| Storage | AWS S3 or Supabase Storage | Object storage for media | 
Frontend: Vercel
# vercel.json
{
  "framework": "vite",
  "buildCommand": "npm run build",
  "outputDirectory": "dist",
  "regions": ["iad1"], # US East (closest to API)
  "env": {
    "VITE_API_URL": "https://api.tvl.com"
  }
}
Cost: $0/mo (Hobby) → $20/mo (Pro for custom domain + analytics)
Backend: Railway
# Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
CMD ["node", "dist/server.js"]
# railway.toml
[build]
builder = "dockerfile"
[deploy]
healthcheckPath = "/health"
healthcheckTimeout = 30
restartPolicyType = "on_failure"
Cost: $5/mo (Hobby) → $20/mo (Pro with autoscaling)
Alternative: Fly.io (similar pricing, slightly more complex)
Database: Supabase
Plan: Free (dev/staging) → Pro $25/mo (production)
Features Used:
- Postgres 15 with RLS
- Connection pooler (PgBouncer)
- Automatic backups
- Supabase Auth (Google OIDC)
Redis: Upstash
Plan: Free (10k commands/day) → Pay-as-you-go $0.20/100k commands
Features:
- Serverless Redis (no idle cost)
- TLS by default
- Global replication (future)
Alternatives Considered
Alternative 1: AWS (ECS + RDS + ElastiCache)
Rejected
Pros:
- Enterprise-grade
- Full control
- Can optimize costs at scale
Cons:
- Steep learning curve (VPC, security groups, IAM)
- High operational overhead
- More expensive ($100-500/mo for MVP)
- Slower time-to-market (2-4 weeks setup)
Decision: Managed platforms faster for MVP; migrate to AWS if needed at scale.
Alternative 2: Single Platform (all on Railway)
Rejected
Pros:
- Simpler (one platform)
- Unified billing
Cons:
- Vercel better for frontend (global CDN, edge functions)
- Railway doesn't have managed Postgres (must self-host)
- Supabase RLS + Auth valuable
Decision: Multi-platform worth the complexity for better DX and features.
Alternative 3: Self-Hosted on DigitalOcean
Rejected
Pros:
- Full control
- Lower cost at scale ($50-100/mo)
Cons:
- Must manage Postgres, Redis, backups, scaling, security
- High ops burden (not suitable for small team)
- No managed Auth or RLS
Decision: Managed services reduce ops burden; worth the cost for MVP.
Consequences
Positive
- 
Speed - Deploy frontend in < 1 minute (Vercel)
- Deploy backend in < 5 minutes (Railway)
- Zero infrastructure setup
 
- 
Developer Experience - Git-based deployments (push to deploy)
- Preview deployments for PRs (Vercel)
- Automatic HTTPS (Let's Encrypt)
 
- 
Cost - Total: ~$50-100/mo for production MVP
- Free tiers for staging/dev
- No upfront costs
 
- 
Scalability - Vercel: global CDN, auto-scaling
- Railway: horizontal scaling (add instances)
- Supabase: connection pooler handles 1000+ connections
 
Negative
- 
Vendor Lock-in - Dependent on 4 platforms (Vercel, Railway, Supabase, Upstash)
- Migration effort if switching
- Mitigation: Use Docker (portable), keep business logic platform-agnostic
 
- 
Cost at Scale - Managed services more expensive than self-hosted at large scale
- Mitigation: Optimize when revenue justifies; enterprise plans available
 
- 
Limited Control - Can't customize infrastructure (e.g., network topology)
- Mitigation: Sufficient for MVP; re-evaluate post-PMF
 
Migration Path
If Scaling Beyond Managed Services:
Phase 1: Optimize Managed ($100-500/mo)
- Upgrade to Pro/Team plans
- Enable autoscaling
- Add read replicas (Supabase)
Phase 2: Hybrid ($500-2000/mo)
- Keep frontend on Vercel (CDN)
- Move backend to AWS ECS
- Keep Supabase (or migrate to AWS RDS)
Phase 3: Fully Self-Hosted ($2000-10000/mo)
- Multi-region AWS
- Kubernetes for orchestration
- Dedicated ops team
Trigger: Revenue > $500k/year or scaling limits hit.
Validation Checklist
- Frontend deploys in < 2 minutes
- Backend deploys in < 5 minutes
- Zero-downtime deployments
- HTTPS enforced on all endpoints
- Health checks configured
- Automatic backups (database)
- Monitoring and alerting (Grafana, Sentry)
- Cost tracking per environment
Sources
- docs/01-architecture/deployment-views.md
- docs/00-overview/platform-overview.md