JC
Julissa Cotillo
Back to blog
brand-julissa-cotillo

How I migrated 3 open-source apps off a flaky VPS in one afternoon — and saved ~$80/mo doing it

May 22, 2026/2 min read

How I migrated 3 open source apps off a flaky VPS in one afternoon and saved ~$80/mo doing it

For months I’d been running three open source apps on a single Coolify VPS: Ghost for my blog, Rocket.Chat for agent notifications, and a Mastra based web scraper that occasionally went feral. It wasn’t catastrophic, just unreliable enough that Google Search Console started flagging 5xx crawl errors and UptimeRobot pinged me awake every few days.

I even built a Cloudflare Worker watchdog that redeployed Coolify automatically after two failed probes. The fact that I needed a watchdog at all was the symptom.

The setup that wasn’t working

Classic noisy neighbor problem. The scraper would burst CPU and memory, Traefik would choke, and Ghost would 504 even while the container claimed to be healthy. I could have thrown more RAM at it, but that felt like paying rent on a house with a cracked foundation.

The decision: open source on managed single tenant pods

I wanted the same open source stack, just without the babysitting. I compared Ghost(Pro), MagicPages, Rocket.Chat Cloud, and Pikapods. Pikapods won: single tenant pods, $3–10 per app, full SFTP access, and the same Docker images I was already using.

What got migrated

1. Ghost (about 30 minutes start to DNS flip)

Ghost’s JSON export/import handled posts, tags, and users perfectly. Images were the only snag. I parsed the JSON for /content/images/... URLs, curled them from the old site, and SFTP uploaded them to the new pod. Two broken images were recovered from a March backup. One Cloudflare API call later, DNS flipped to the new pod. The only surprise: Ghost refused requests for blog.practical.works until I updated the url env var. One line fixed it.

2. Rocket.Chat (the tricky one)

Rocket.Chat stores everything in MongoDB. The old instance was crash looping but Mongo was fine, so I mongodump ed 3,800 messages and 19 users. Pikapods doesn’t allow shell access, only SFTP, which meant no mongorestore on the destination. The workaround: restore locally, drop ephemeral collections, then upload the raw WiredTiger data directory into the pod’s mongo data/ folder. Matching Mongo 7.0’s WiredTiger version was key. When the pod restarted, every message and webhook was intact. Six months of audit history saved.

3. Inngest (deferred but prepped)

Inngest was part of the original Coolify mess. I’m moving it to Pikapods next (server + Postgres + Redis, about $10/mo). For now I throttled the noisiest crons to cut monthly executions from ~500k to ~165k, buying time to decide between Cloud Pro and self hosted.

The cost math

WorkloadSaaS equivalentPikapods cost
GhostGhost(Pro) $18/mo$3/mo
Rocket.ChatSlack Pro $22/mo (3 users)$8/mo
InngestInngest Cloud Pro $75/mo~$10/mo (planned)
Mastra scraperApify/Browserbase $50+/mo~$5/mo (planned)

Savings: roughly $80–100 per month, plus Cloudflare R2 backups stay in the free tier.

What I’d do differently

  • Verify SFTP based restore feasibility before committing.
  • Configure backups on day one, not day three.
  • Don’t trust “Running (healthy)” without tailing logs.
  • Expect migrations to surface latent bugs. My Ghost key rotation exposed a broken build that had been silently failing for days.

Why open source self hosted still matters in 2026

It’s not just about cost. With SaaS, I don’t own my data, can’t customize deeply, and live at the mercy of pricing changes. With managed open source pods, the data is mine, the code is upstream, and the pricing is predictable. If Pikapods vanished tomorrow, I could spin the same containers anywhere.

That portability, the ability to take my data and run, is worth far more than the $80 a month I saved.