Skip to content

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)

  1. Remove GoTrue deployment - Already replaced by Authentik
  2. Remove Studio/postgres-meta - Use pgAdmin or DBeaver locally
  3. Document: New apps should use Drizzle - Stop expanding PostgREST dependency

Short-Term (If RAM Constrained)

  1. Remove unused services (~270 MB savings)
  2. Consider migrating smallest app (brainlearn) as proof-of-concept

Long-Term (If Scaling)

  1. Migrate apps incrementally during major refactors
  2. Start with low-complexity apps (home-portal, brainlearn)
  3. 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


Changelog

Date Change
2025-12-23 Initial evaluation created