Most teams pay $50-200/month for staging databases. Neon's free tier gives you 10 branches of serverless Postgres — and branches are instant copy-on-write clones. That's a free preview database per PR.
Here's the whole setup in one GitHub Action.
Why this is absurd value
| Provider | Preview DB | Cost |
|---|---|---|
| Heroku Postgres | $50/mo (mini) | $50 |
| RDS staging | ~$30/mo | $30 |
| Neon branch | 10 branches free | $0 |
Branch creation: ~1 second, regardless of DB size (copy-on-write). Storage: 0.5GB free per branch, 3GB project total.
The Action
# .github/workflows/preview-db.yml
on: [pull_request]
jobs:
preview-db:
runs-on: ubuntu-latest
steps:
- name: Create Neon branch
id: branch
run: |
BR=$(curl -s -X POST \
"https://console.neon.tech/api/v2/projects/$NEON_PROJECT_ID/branches" \
-H "Authorization: Bearer $NEON_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"branch\": {\"name\": \"pr-${{ github.event.pull_request.number }}\"}}" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['branch']['id'])")
echo "branch_id=$BR" >> $GITHUB_OUTPUT
env:
NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
NEON_PROJECT_ID: ${{ secrets.NEON_PROJECT_ID }}
- name: Run migrations against branch
run: |
alembic upgrade head # or prisma migrate deploy
env:
DATABASE_URL: postgres://user:pass@ep-xxx-pooler.region.aws.neon.tech/db?options=endpoint%3D${{ steps.branch.outputs.branch_id }}
Delete the branch on PR close with one more curl -X DELETE. Done.
The gotcha nobody mentions
Branches share the project's compute quota (300 CU-hours free/month — about 190 hours of an always-on 0.25 CU instance). If your CI runs migrations 40 times a day, enable scale-to-zero (suspend_timeout_seconds: 300) or you'll burn through it in week two.
I run this for two side projects. Monthly bill so far: $0. My old Heroku staging bill: $50/month.
More free-infra setups I've battle-tested: https://ly.cyberserval.tech/iIETXiF
Have you wired preview DBs into CI, or does your team still share one staging database and pray? What broke first?
Top comments (0)