The Fly.io Honeymoon Ends
Fly.io is genuinely impressive engineering. Anycast routing, Firecracker microVMs, global distribution with a simple fly deploy. For prototypes and small projects, it's great.
Then your app grows. You add a database. A few workers. Maybe some cron jobs. And Fly's per-machine pricing starts compounding in ways the pricing page doesn't make obvious.
We hit $7,370/month for a financial data platform. Not because we were wasteful — we'd actually worked hard to optimize. That was the efficient number. We migrated to bare metal and now pay $460/month for identical workloads, with better p99 latency.
Fly.io Pricing: What You Actually Pay
Fly charges per machine, per second. The advertised prices look reasonable. The actual bill doesn't.
| What You're Running | Fly.io/month | RAW/month | Savings |
|---|---|---|---|
| 1× 2 vCPU / 4GB (web) | $62 | $0 | 100% |
| 1× 4 vCPU / 8GB (API) | $124 | $11 | 91% |
| 1× 8 vCPU / 16GB (worker) | $248 | $21 | 92% |
| Postgres (8 vCPU / 16GB) | $248 | $21 | 92% |
| Redis (shared) | $30 | $0 (self-hosted) | 100% |
| Egress (500GB/mo) | $45 | $0 | 100% |
| Total | $757 | $53 | 93% |
Fly.io prices as of April 2026, shared CPU pricing. Egress at $0.09/GB. RAW uses Hetzner bare metal infrastructure.
Fly.io's Hidden Costs
The per-machine price is just the start:
- Egress. $0.09/GB outbound, $0.02/GB between regions. An API pushing 500GB/month adds $45. We push 3TB. That was $270/month just in egress.
- Persistent volumes. $0.15/GB/month. A 100GB Postgres volume = $15/month on top of the machine cost.
- IPv4 addresses. $2/month per dedicated IPv4. Adds up when you have 8+ machines.
- Support. Real support starts at $299/month. The free tier is GitHub Discussions.
- Minimum billing. Even suspended machines that you "stopped" might still incur charges if Fly keeps them warm.
What Fly.io Does Well
Being fair: some things Fly actually does better.
- Global edge. 35+ regions with smart routing. If you need <50ms latency in Tokyo, São Paulo, and London simultaneously, Fly's anycast setup is genuinely good.
- Zero-config TLS. Certificates just appear. No Certbot setup required.
- Scale-to-zero. If you have services with wildly intermittent traffic, not paying for idle time is real value.
- Simple DX.
fly deployis a great command. The developer experience is polished.
The problem is you pay a 10x premium for these features whether you use them or not. If your app runs in one or two regions and doesn't need sub-50ms global latency, you're paying for infrastructure you don't need.
Who Should Leave Fly.io
- You're spending more than $100/month on Fly
- Your app runs primarily in 1-2 regions
- You don't need scale-to-zero (most production apps don't)
- You have a database or stateful service on Fly (these get expensive fast)
- You're tired of the bill being unpredictable
Migration Guide: Fly.io → RAW
We migrated a 6-service app in about 4 hours. Here's the playbook.
Step 1: Deploy your RAW server
$ npx rawhq deploy
# Pick your region (Frankfurt, Helsinki, etc.)
# Choose your plan (free tier: 2vCPU/4GB)
# SSH key auto-configured
# Server ready in ~45 secondsStep 2: Install Docker + Caddy
# Install Docker
curl -fsSL https://get.docker.com | sh
# Install Caddy (handles TLS automatically, like Fly)
apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update && apt install caddyStep 3: Convert fly.toml to docker-compose.yml
# fly.toml (what you have)
[build]
dockerfile = "Dockerfile"
[[services]]
internal_port = 3000
protocol = "tcp"
[[services.ports]]
port = 443
handlers = ["tls", "http"]
# docker-compose.yml (what you need)
services:
app:
build: .
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
env_file: .env
# Caddyfile (replaces Fly's TLS)
yourdomain.com {
reverse_proxy localhost:3000
}Step 4: Migrate your database
# Dump from Fly Postgres
fly proxy 5432 -a your-postgres-app &
pg_dump -h localhost -U postgres your_db > dump.sql
# Copy to new server
scp dump.sql root@your-raw-server:/root/
# Restore on bare metal
docker run -d --name postgres \
-e POSTGRES_PASSWORD=yourpassword \
-v pgdata:/var/lib/postgresql/data \
-p 127.0.0.1:5432:5432 \
postgres:16
psql -h localhost -U postgres < dump.sqlStep 5: DNS cutover
# Point your domain to your new server IP
# Your new server IP is in the RAW dashboard
A yourdomain.com → 195.x.x.x
# Caddy handles Let's Encrypt automatically
# Zero-downtime: DNS TTL determines switch speed
# Set TTL to 60s before migration for fast rollbackWhat You Give Up
Be honest with yourself before migrating:
- Multi-region out of the box. Fly handles this natively. On bare metal you'd need a load balancer + multiple servers. Still cheaper at scale, but more work.
- Scale-to-zero. Your server runs 24/7 even at idle. At $21/month, that's fine. At $200/month, you'd reconsider.
- Managed Postgres. You're running your own database now. Set up automated backups (we use
pg_dumpto S3 via cron).
The Bottom Line
Fly.io is a legitimate product. It's just priced for VC-funded startups who value developer experience over unit economics. Once you're past the prototype stage and you know what you need, bare metal gives you the same deploys at 10% of the cost.
The migration takes half a day. The savings pay for an engineer's time inside a month.
Try It
$ npx rawhq deployDeploy Free Server →