Supabase Stack Evaluation¶
Evaluation of Supabase's value in the Tower Fleet stack and analysis of potential migration to a simpler PostgreSQL + ORM architecture.
Created: 2025-12-23 Status: Evaluation Complete Priority: Low (Backlog) Decision: Keep Supabase for now; use Drizzle for new apps
Executive Summary¶
Supabase provides convenience through PostgREST's auto-generated API, but we're only using a subset of its features. With Authentik replacing GoTrue for auth, the value proposition has shifted. Migration to direct PostgreSQL + Drizzle is feasible but represents significant effort (~60-80 hours across all apps) for modest resource savings (~1.4 GB RAM).
Recommendation: Maintain current stack, but adopt Drizzle for new applications to reduce PostgREST dependency growth.
Current Supabase Usage¶
Services Running¶
| Service | Purpose | RAM (Actual) | RAM (Limit) | Status |
|---|---|---|---|---|
| PostgreSQL | Database | 385 Mi | 2 Gi | Keep |
| PgBouncer | Connection pooling | 2 Mi | 128 Mi | Keep |
| PostgREST | Auto REST API | 31 Mi | 512 Mi | In Use |
| Kong | API Gateway | 392 Mi | 512 Mi | In Use |
| GoTrue | Auth service | 15 Mi | 256 Mi | Unused (Authentik) |
| Storage | File storage | 128 Mi | 512 Mi | Light use |
| postgres-meta | DB introspection | 134 Mi | 256 Mi | Studio only |
| Studio | Admin dashboard | 123 Mi | 512 Mi | Optional |
Total actual usage: ~1.2 GB (production) + ~1.2 GB (sandbox) = ~2.4 GB
Features by Usage Level¶
| Feature | Usage | Notes |
|---|---|---|
| PostgreSQL | Heavy | Core data layer, RLS, schema isolation |
| PostgREST | Heavy | All apps use .from() queries |
| GoTrue (Auth) | None | Replaced by Authentik SSO |
| Storage | Light | 2 apps (trip-planner, subtitleai) |
| Realtime | None | Not used |
| Edge Functions | None | Not used |
| Studio | Low | Occasional admin tasks |
Per-App Supabase Usage¶
| App | Tables | Queries | Storage | Complexity |
|---|---|---|---|---|
| money-tracker | 7 | ~25 | No | Medium |
| home-portal | 2 | ~3 | Yes (icons) | Low |
| trip-planner | 6 | ~30 | Yes (GPX) | High |
| rms | 8 | ~30 | No | Medium |
| brainlearn | 3 | ~4 | No | Low |
| subtitleai | 3 | ~5 | Yes | Low |
Alternatives Considered¶
Option 1: Plain PostgreSQL + Drizzle ORM¶
Architecture:
Apps → Drizzle ORM → PostgreSQL (direct)
→ MinIO/S3 for file storage
→ Authentik for auth (already done)
Pros: - Simpler architecture (fewer services) - Better TypeScript integration - Full ORM features (relations, migrations) - ~1.4 GB RAM savings
Cons: - Lose auto-generated REST API - Must write more data access code - Significant migration effort
Option 2: PocketBase¶
Architecture: Single Go binary with SQLite
Pros: - Extremely simple (~30MB binary) - Built-in auth, storage, realtime - ~50MB RAM total
Cons: - SQLite-based (not PostgreSQL) - Would require complete data migration - Different query patterns - No multi-schema isolation
Verdict: Not suitable for existing PostgreSQL investment
Option 3: Keep Supabase, Remove Unused Services¶
Architecture: Keep Kong + PostgREST + Storage, remove GoTrue/Studio
Pros: - Minimal disruption - ~250MB RAM savings - Keep existing patterns
Cons: - Still running more than needed - Ongoing maintenance of Supabase stack
Migration Effort Estimate¶
Per-App Migration Tasks¶
| Task | Hours | Notes |
|---|---|---|
| Create Drizzle schema | 2-3 | Define tables, relations |
| Convert server queries | 3-4 | Server components |
| Convert client queries | 4-6 | Client components, hooks |
| Replace storage calls | 1-2 | If using Supabase Storage |
| Remove Supabase deps | 0.5 | Package cleanup |
| Testing & fixes | 2-3 | Integration testing |
Total Effort by App¶
| App | Estimated Hours |
|---|---|
| money-tracker | 12-16 |
| home-portal | 6-8 |
| trip-planner | 16-20 |
| rms | 14-18 |
| brainlearn | 4-6 |
| subtitleai | 8-10 |
| Total | 60-80 hours |
Sample Code Conversion¶
Before (Supabase/PostgREST):
const { data } = await supabase
.from('transactions')
.select('*, category:categories(*)')
.eq('account_id', accountId)
.order('date', { ascending: false })
After (Drizzle):
const data = await db.query.transactions.findMany({
where: eq(transactions.accountId, accountId),
with: { category: true },
orderBy: desc(transactions.date)
})
Resource Savings Analysis¶
If Fully Migrated¶
| Component | Current | After Migration |
|---|---|---|
| PostgreSQL | 385 Mi | 385 Mi (keep) |
| PgBouncer | 2 Mi | 2 Mi (keep) |
| Kong | 392 Mi | 0 (remove) |
| PostgREST | 31 Mi | 0 (remove) |
| GoTrue | 15 Mi | 0 (remove) |
| Storage | 128 Mi | 128 Mi (or MinIO) |
| postgres-meta | 134 Mi | 0 (remove) |
| Studio | 123 Mi | 0 (remove) |
| Total | ~1.2 GB | ~515 MB |
Savings per namespace: ~700 MB Total savings (prod + sandbox): ~1.4 GB
Partial Optimization (Without Migration)¶
Remove only unused services: - GoTrue: 15 Mi - postgres-meta: 134 Mi - Studio: 123 Mi
Quick savings: ~270 MB (no app changes required)
Decision Matrix¶
| Factor | Keep Supabase | Migrate to Drizzle |
|---|---|---|
| RAM usage | 2.4 GB | 1.0 GB |
| Dev velocity | Faster (auto API) | Slightly slower |
| Type safety | Good | Better (native TS) |
| Stack complexity | 8 services | 3 services |
| Storage solution | Built-in | Need MinIO/S3 |
| Initial effort | 0 hours | 60-80 hours |
| Maintenance | Supabase updates | Standard PG + ORM |
| Learning curve | Known | New patterns |
Recommendations¶
Immediate Actions (No Migration)¶
- Remove GoTrue deployment - Already replaced by Authentik
- Remove Studio/postgres-meta - Use pgAdmin or DBeaver locally
- Document: New apps should use Drizzle - Stop expanding PostgREST dependency
Short-Term (If RAM Constrained)¶
- Remove unused services (~270 MB savings)
- Consider migrating smallest app (brainlearn) as proof-of-concept
Long-Term (If Scaling)¶
- Migrate apps incrementally during major refactors
- Start with low-complexity apps (home-portal, brainlearn)
- Leave complex apps (trip-planner) until natural refactor opportunity
For New Applications¶
Use this stack instead of Supabase: - Database: PostgreSQL (shared instance, new schema) - ORM: Drizzle with type-safe queries - Auth: Authentik via NextAuth (already standard) - Storage: MinIO or keep Supabase Storage service
FOSS Alternatives Reference¶
| Solution | License | Database | Best For |
|---|---|---|---|
| Drizzle | Apache 2.0 | PostgreSQL | ORM replacement |
| PocketBase | MIT | SQLite | New simple apps |
| Directus | GPL v3 | PostgreSQL | CMS-style apps |
| Hasura | Apache 2.0 | PostgreSQL | GraphQL-first |
| PostgREST | MIT | PostgreSQL | REST API only |
Related Documentation¶
Changelog¶
| Date | Change |
|---|---|
| 2025-12-23 | Initial evaluation created |