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 RunningFly.io/monthRAW/monthSavings
1× 2 vCPU / 4GB (web)$62$0100%
1× 4 vCPU / 8GB (API)$124$1191%
1× 8 vCPU / 16GB (worker)$248$2192%
Postgres (8 vCPU / 16GB)$248$2192%
Redis (shared)$30$0 (self-hosted)100%
Egress (500GB/mo)$45$0100%
Total$757$5393%

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 deploy is 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 seconds

Step 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 caddy

Step 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.sql

Step 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 rollback

What 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_dump to 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 →