Another email from Supabase. “Your project has been paused due to inactivity.”
I run a few small side projects. Nothing fancy, nothing with thousands of users. Just apps I built for myself and friends. And every few weeks, I’d get that email. My database was paused. Again.
I’d have to log in, click “restore,” wait a few minutes, and hope nothing broke. It was free, sure. But free was costing me patience.
So I did what any reasonable developer would do: I mass-migrated everything to a single €5/month VPS. No more pausing. No more vendor lock-in. Full control.
Here’s how I did it, step by step.
The Old Setup
Before the migration, my stack looked like this:
- Hosting: Vercel (free tier)
- Database: Supabase (free tier, hence the pausing)
- Analytics: Vercel Analytics
- Cost: €0
It worked fine. Until it didn’t.
The New Setup
After the migration:
- VPS: Hetzner CX32 (4 vCPU, 8GB RAM, 80GB SSD), €5/month
- Deployment: Coolify (self-hosted Vercel alternative)
- Database: Pocketbase (SQLite-based, ~30MB RAM)
- Analytics: Umami (self-hosted, privacy-friendly)
- Cost: €5/month for everything
One server. Multiple apps. No pausing. No cold starts. No “you’ve exceeded your serverless function invocations” nonsense.
Phase 1: Setting Up the VPS
I went with Hetzner because they’re cheap and their servers are solid. I picked a CX32, probably overkill for my needs, but I wanted headroom.
The setup was stupidly simple:
- Create a Hetzner account
- Spin up an Ubuntu 24.04 server
- Add my SSH key
- SSH in and run one command:
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
That’s it. Coolify installs itself, sets up Docker, configures Traefik for SSL, and gives you a web dashboard. Five minutes, tops.
Phase 2: Understanding Coolify
Coolify is what Vercel would be if Vercel let you own your infrastructure. You get:
- Git-based deployments (push to main, it builds and deploys)
- Automatic SSL certificates via Let’s Encrypt
- A nice web UI to manage everything
- Support for databases, Docker images, static sites, whatever
The first time I opened the dashboard at http://my-server-ip:8000, I felt like I’d been handed the keys to a small kingdom.
I connected my GitHub account (they use a GitHub App for webhooks, so deploys are instant) and pointed a subdomain at my server. Done.
Phase 3: Migrating from Vercel
My personal website is an Astro static site. Migration took about 3 minutes:
- Remove
@vercel/analyticsfrom the code (it won’t work outside Vercel anyway) - In Coolify: Add Resource → GitHub → Select repo
- Enable “Static Site”
- Set the publish directory to
dist - Add my domain
- Deploy
Coolify detected it was an Astro project, ran npm install && npm run build, and served the static files. SSL certificate appeared automatically. My site was live on my own server.
One down. Several to go.
Phase 4: Replacing Supabase with Pocketbase
This was the bigger undertaking. I had an app called Padelica, a padel match organizer I built for my friend group. It used Supabase for the database with real-time subscriptions.
Pocketbase is an open-source backend in a single file. It gives you:
- A SQLite database
- REST API automatically generated from your schema
- Real-time subscriptions (WebSocket-based)
- An admin UI to manage everything
- Authentication (which I wasn’t using, but nice to have)
It runs on about 30MB of RAM. Ridiculous.
Deploying Pocketbase
In Coolify:
- Add Resource → Docker Image
- Image:
ghcr.io/muchobien/pocketbase:latest - Add a volume mount for
/pb_data(so your data survives restarts) - Expose port
8090 - Add a domain like
db.myapp.com - Deploy
I opened https://db.myapp.com/_/, created an admin account, and started setting up collections.
Creating the Schema
Pocketbase has a nice UI for creating collections (tables). I recreated my Supabase schema:
games: stores match infoplayer_responses: who can play whenplayer_preferences: preferred time slotsconfirmed_players: final roster
Each collection took about 30 seconds to set up. Relations work by creating a “Relation” field type and pointing it at another collection.
The Code Migration
This was the tedious part. Supabase and Pocketbase have different APIs:
// Supabase
const { data } = await supabase
.from('games')
.select('*')
.eq('invite_token', token)
.single()
// Pocketbase
const data = await pb.collection('games')
.getFirstListItem(`invite_token="${token}"`)
Not dramatically different, but different enough that I had to touch every database call. The real-time subscriptions also changed:
// Supabase
supabase.channel('games').on('postgres_changes', { ... }, callback)
// Pocketbase
pb.collection('games').subscribe('*', callback)
Pocketbase’s syntax is actually cleaner. The migration took maybe an hour of find-and-replace work.
API Rules (Important!)
By default, Pocketbase locks down all collections. Only admins can access them. Since my app doesn’t have user authentication, I needed to open up the API:
- Go to each collection
- Click the gear icon → API Rules
- Set List, View, Create, Update, Delete to empty (which means “allow everyone”)
I also enabled rate limiting in Pocketbase settings. Two requests per second for creates, 300 per 10 seconds for reads. Enough to stop abuse, not enough to block legitimate use.
Phase 5: Self-Hosted Analytics with Umami
Vercel Analytics was nice, but it only works on Vercel. I needed something self-hosted.
Umami is the obvious choice. It’s lightweight, privacy-focused (no cookies, GDPR-compliant by default), and the UI is clean.
Deploying Umami
Umami needs a PostgreSQL database. In Coolify:
- Add Resource → Database → PostgreSQL
- Note the connection string
- Add Resource → Docker Image
- Image:
ghcr.io/umami-software/umami:postgresql-latest - Set environment variable:
DATABASE_URL=postgresql://... - Expose port
3000 - Add domain
analytics.mydomain.com - Deploy
First login is admin / umami. Change this immediately.
Adding the Tracking Script
In Umami, I added each of my websites and got a tracking script:
<script defer src="https://analytics.mydomain.com/script.js"
data-website-id="abc123"></script>
Dropped it in the <head> of each site. Done. I now have analytics that I own, on data that never leaves my server.
Phase 6: Security Hardening
With everything running on one box, I wanted to lock it down:
Firewall
ufw allow 22 # SSH
ufw allow 80 # HTTP
ufw allow 443 # HTTPS
ufw enable
Pocketbase Rate Limiting
Enabled in Settings → Rate Limiting. Prevents someone from spamming my database with garbage.
Strong Passwords
Coolify admin, Pocketbase admin, Umami admin. All unique, all stored in a password manager.
SSH Keys Only
Disabled password authentication for SSH. If you don’t have my key, you’re not getting in.
The Final Tally
Before:
- Vercel: Free (but serverless cold starts)
- Supabase: Free (but constant pausing)
- Analytics: Vercel (locked to their platform)
- Control: None
- Monthly cost: €0
After:
- Hetzner VPS: €5/month
- Coolify: Free (self-hosted)
- Pocketbase: Free (self-hosted)
- Umami: Free (self-hosted)
- Control: Total
- Monthly cost: €5
For the price of two coffees, I have a setup that:
- Never pauses
- Never cold starts
- Gives me full access to my data
- Can host as many apps as I want
- Runs analytics without selling data to anyone
Was It Worth It?
Look, the free tier of Vercel and Supabase is genuinely good for getting started. If you’re building your first project and need to ship fast, use them. Don’t let infrastructure slow you down.
But once you have a few projects running, once you start hitting the limits, once you get that third “your project has been paused” email… consider this path. It’s not as scary as it sounds. Modern tools like Coolify have made self-hosting almost as easy as the managed platforms.
The best part? When something breaks at 2 AM, I can actually fix it myself. No support tickets. No waiting. Just SSH, logs, and the satisfaction of true ownership.
My projects don’t pause anymore. And neither does my patience.