Last December I was paying $15/month for a managed Postgres instance that hosted four side projects with a combined ~2,300 monthly users. The DB averaged 4% CPU. I was burning $180/year on an idle server because "that's just what Postgres costs."
Then I moved everything to Neon's free tier. Six months later: $0 paid, zero data loss, one real limitation. Here's the honest breakdown.
The Numbers After 6 Months
| Metric | Paid host ($15/mo) | Neon free tier |
|---|---|---|
| Monthly cost | $15 | $0 |
| Storage used | 1.9 GB | 1.9 GB (limit: 0.5 GB* — see below) |
| Compute-hours/month | 730 (always on) | ~41 (autosuspend) |
| Query p50 latency | 3.1 ms | 3.8 ms |
| Cold-start penalty | none | +380 ms after idle |
| Downtime incidents | 1 (their maintenance) | 0 |
* Storage is the trap. Free tier gives you 0.5 GB per branch, 10 branches. My 1.9 GB didn't fit on one branch — I split projects across branches (each project = one branch). That's not a hack, it's literally the intended architecture, and branching turned out to be the best feature: every deploy previews against a real copy of prod data.
The Autosuspend Thing Everyone Complains About
Neon scales to zero after ~5 minutes idle. First query after suspend pays a cold start. I measured it properly, from wrk over 200 cold starts:
median cold start: 381 ms
p95 cold start: 612 ms
warm p50: 3.8 ms
For a dashboard or admin tool: completely irrelevant. For a user-facing API where the first request of the morning matters: I added a cron that pings the API every 4 minutes (free on Cloudflare Workers). Cold starts effectively disappeared — compute usage went from ~30 to ~41 compute-hours/month, still nowhere near the free 191.9 limit.
The Migration (actually took one evening)
# dump from old host
pg_dump -h old-host -U app -d appdb --no-owner -Fc > appdb.dump
# restore into Neon (connection string from dashboard)
pg_restore -h ep-xxx.us-east-2.aws.neon.tech -U app -d appdb \
--no-owner --clean --if-exists appdb.dump
The only code change was the connection string plus enabling connection pooling — Neon's free tier caps direct connections, so you go through their pooler endpoint (-pooler in the hostname). With SQLAlchemy:
engine = create_engine(
"postgresql+psycopg2://app:***@ep-xxx-pooler.us-east-2.aws.neon.tech/appdb",
pool_size=5, max_overflow=0, pool_pre_ping=True,
)
pool_pre_ping=True matters — idle connections to a suspended compute get recycled, and without pre-ping you'll see the occasional SSL connection has been closed on the first query.
Where the Free Tier Genuinely Ends
I'd pay again if any of these were true:
- A single project over 0.5 GB — branch-splitting is fine for separate apps, not for one big database. First paid tier is $19/month and the jump stings.
- Sustained traffic 24/7 — if your app never idles, you lose the autosuspend advantage and the compute-hours cap becomes the ceiling.
- SOC 2 / compliance requirements — that's enterprise plan territory.
The Controversial Take
Most side projects should never pay for a database, and paying early is a form of procrastination. We tell ourselves $15/month is "serious infrastructure," but it's usually just insurance against a scaling problem we don't have. My four projects would need roughly 20x growth before any free-tier limit becomes a real business problem — and if that happens, paying $19/month will be the best problem I've ever had.
Between this and rewriting both apps' queries with MonkeyCode (it caught an N+1 in my dashboard endpoint that was 40% of my query time), my entire backend stack now costs $0/month.
What's the dumbest thing you're still paying monthly for? Mine was a 4%-CPU Postgres. Beat that.
Top comments (0)