I had 7 side projects on AWS RDS. None had more than 100 users. All were quietly costing me $6-8/month each — $47/month for databases that served maybe 50 requests per day combined.
I spent a weekend migrating them all to free-tier Postgres. Here's what happened.
The inventory (before)
| Project | DB size | Requests/day | RDS cost |
|---|---|---|---|
| URL shortener | 12MB | ~20 | $6.80 |
| Blog comments | 3MB | ~5 | $6.80 |
| Habit tracker | 8MB | ~15 | $6.80 |
| Recipe box | 45MB | ~30 | $7.20 |
| Weather cache | 120MB | ~50 | $8.40 |
| API status page | 2MB | ~10 | $6.80 |
| Wedding RSVP (archived) | 5MB | 0 | $6.80 |
| Total | 195MB | ~130 | $47.60 |
The migration targets
- Neon: 0.5GB free, autosuspend, branching. Best for projects that need to wake fast.
- Supabase: 500MB free, connection pooler, auth built-in. Best for projects needing auth.
- Turso: 9GB free, sqlite-vec, edge replicas. Best for read-heavy, tiny latency.
The migration script (works for any Postgres)
#!/bin/bash
# migrate.sh — dump from RDS, restore to free tier
set -e
SOURCE="postgres://user:pass@rds.amazonaws.com:5432/dbname"
TARGET="postgres://user:pass@ep-xxx.neon.tech/dbname"
# Dump with no owner, no privileges (cleanest for free tiers)
pg_dump "$SOURCE" \
--no-owner --no-privileges --no-tablespaces \
--format=custom --file=/tmp/dump.backup
# Restore
pg_restore --dbname="$TARGET" --no-owner --no-privileges /tmp/dump.backup
echo "Done. Verify row counts:"
psql "$TARGET" -c "SELECT schemaname, relname, n_live_tup FROM pg_stat_user_tables;"
Gotchas that cost me 3 hours
Neon autosuspend kills long queries. My recipe box had a materialized view refresh that took 90 seconds. Neon killed it at 30s. Fix:
SET idle_in_transaction_session_timeout = 0and run refreshes during active hours.Supabase pooler drops idle connections. My habit tracker's background worker used a persistent connection. After 10 minutes idle, Supabase dropped it. Fix: switch to connection pooler in transaction mode, or use pgbouncer.
Turso is SQLite, not Postgres. My weather cache used
jsonb_path_query. Turso doesn't have it. I rewrote to plain JSON functions. Not hard, but not a drop-in replacement.
The results (after 30 days)
| Project | New home | Cost | Uptime | Notes |
|---|---|---|---|---|
| URL shortener | Neon | $0 | 100% | Cold start ~300ms, fine |
| Blog comments | Supabase | $0 | 100% | Auth integration was free |
| Habit tracker | Neon | $0 | 99.2% | One 4-min outage during region failover |
| Recipe box | Turso | $0 | 100% | Fastest of all, edge replicas help |
| Weather cache | Turso | $0 | 100% | JSON rewrite took 1 hour |
| API status page | Supabase | $0 | 100% | Cron pings keep it warm |
| Wedding RSVP | Neon | $0 | 100% | Dormant, autosuspend is perfect |
Total: $0/month. Savings: $571/year.
The controversial part
AWS RDS free tier is a trap for side projects. 12 months free, then $6-8/month per project forever. The free tiers from Neon, Supabase, and Turso are permanent and generous enough for anything under 100 daily users. The lock-in isn't technical — it's the migration script above. Run it once, you're free.
Migration configs and monitoring scripts drafted with MonkeyCode: https://ly.cyberserval.tech/iIETXiF
Which project are you keeping on paid RDS that could be free? And what's actually stopping you from migrating it this weekend?
Top comments (0)