The Fly.io Problem

Fly.io launched with an elegant pitch: deploy your app globally with one command. Run containers at the edge. Scale to zero. It sounded perfect.

Then the bills arrived.

Fly.io's pricing model is usage-based with multiple dimensions: CPU time, memory allocation, persistent volumes, outbound bandwidth, IPv4 addresses, and Fly Machines uptime. Each one has its own meter. Each one can spike. And their free tier is deliberately small enough to force upgrades within weeks.

Developers who expected $5-10/mo bills found themselves paying $30-80/mo for a single app. Add a database, a Redis cache, and a background worker, and you're north of $150/mo for infrastructure that would cost $11 on bare metal.

Fly.io Pricing Breakdown (Real Costs)

  • Shared CPU 1x, 256 MB: $1.94/mo (barely enough for a health check endpoint)
  • Shared CPU 1x, 1 GB: $5.70/mo (minimum viable for a Node.js app)
  • Performance 1x, 2 GB: $29.00/mo (what most production apps need)
  • Performance 2x, 4 GB: $58.00/mo
  • Persistent Volume: $0.15/GB/mo (need 20 GB? add $3/mo)
  • Dedicated IPv4: $2/mo per app
  • Outbound bandwidth: $0.02/GB after 100 GB free
  • Fly Postgres: Runs as another Machine, doubling your compute cost

A typical production setup on Fly.io (app + database + Redis + worker) runs $80-150/mo. And that's for a single region.

Cold Starts: The Hidden Performance Tax

Fly.io's "scale to zero" feature sounds great for saving money. In practice, it means your app has cold starts. When a request hits a stopped Machine, Fly.io needs to boot it. That takes 300ms-2s depending on your image size and language runtime.

For APIs, that's unacceptable. For web apps, your first visitor after idle gets a blank screen while the container spins up. You can disable scale-to-zero, but then you're paying 24/7 for compute whether you use it or not.

On bare metal, your process is always running. There is no cold start. Your app responds in single-digit milliseconds, every time, day or night.

Edge Deployment vs Bare Metal Performance

Fly.io's value proposition is edge deployment: run your app in 30+ regions close to users. Sounds fast. But there's a catch.

Edge instances are shared-CPU microVMs. They're small, they're noisy-neighbor-prone, and they compete for resources. A single bare metal server in Frankfurt often outperforms three Fly.io edge instances combined, because it has dedicated CPU, dedicated RAM, and NVMe storage with consistent IOPS.

MetricRAW Bare MetalFly.io (Performance 1x)
TTFB (p50)12ms45ms
TTFB (p99)38ms890ms (cold start)
Disk I/O (seq read)3,200 MB/s (NVMe)~400 MB/s (volume)
CPU (single-core)DedicatedShared (noisy neighbor)
Memory4 GB dedicated2 GB (capped)
Concurrent connections10,000+~1,000 (before throttle)

Tested with k6 from Frankfurt. RAW raw-2 ARM ($6/mo) vs Fly.io Performance 1x 2GB ($29/mo).

Unless your users are spread across 6+ continents and latency below 50ms matters, a single well-placed bare metal server will outperform and outlast a fleet of Fly.io edge instances.

RAW vs Fly.io: Full Comparison

FeatureRAWFly.io
2 vCPU / 4 GB$6/mo$29/mo (Performance 1x 2GB) + extras
4 vCPU / 8 GB$11/mo$58/mo + extras
Bandwidth20 TB included100 GB free, then $0.02/GB
Storage40-160 GB NVMe included$0.15/GB/mo (extra)
DatabaseSelf-host ($0)Separate Machine ($15-60/mo)
Cold startsNone (always running)300ms-2s
Root accessFull SSHNo (container only)
IPv4Included$2/mo per app
Pricing modelFlat monthlyUsage-based (variable)
Vendor lock-inNone (standard Linux)fly.toml, Fly Machines API

What Fly.io Does Better

To be fair, Fly.io has genuine strengths:

  • Multi-region deployment: If you need your app in Tokyo, Sao Paulo, and Amsterdam simultaneously, Fly.io makes this easy
  • Anycast networking: Automatic routing to the nearest region
  • Built-in WireGuard: Private networking between Machines
  • flyctl CLI: Genuinely good developer experience for container deployment

If you're building a globally distributed real-time app (think multiplayer games or collaborative editors), Fly.io's multi-region story is compelling. For everything else, you're paying a premium for infrastructure you don't need.

Migration Guide: Fly.io to RAW

Moving off Fly.io takes about 30 minutes. Here's the step-by-step:

Step 1: Provision a RAW Server

# Deploy a bare metal server (Ubuntu 24.04)
npx rawhq deploy

# SSH in
ssh root@your-server-ip

# Update system
apt update && apt upgrade -y

Step 2: Install Your Runtime

# For Node.js apps
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt install -y nodejs
npm install -g pm2

# For Docker-based apps (same as Fly.io)
curl -fsSL https://get.docker.com | sh

Step 3: Extract Your Fly.io Config

Your fly.toml contains the environment variables and port mappings you need. Extract them:

# Check your current Fly.io config
cat fly.toml

# Note down:
# - Internal port (usually 8080 or 3000)
# - Environment variables
# - Health check path
# - Volume mounts (if any)

Step 4: Deploy Your App

# Clone your repo
git clone https://github.com/youruser/yourapp.git /var/www/app
cd /var/www/app

# Set environment variables
cat > .env << 'EOF'
NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
EOF

# Install and build
npm ci
npm run build

# Start with PM2
pm2 start npm --name "app" -- start
pm2 save
pm2 startup systemd

Step 5: Set Up Nginx + SSL

# Install Nginx and Certbot
apt install -y nginx certbot python3-certbot-nginx

# Create Nginx config
cat > /etc/nginx/sites-available/app << 'EOF'
server {
    listen 80;
    server_name yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
EOF

ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

# Get SSL certificate
certbot --nginx -d yourdomain.com

Step 6: Update DNS

# Point your domain to the new server
# A record: yourdomain.com -> your-server-ip
# AAAA record: yourdomain.com -> your-server-ipv6 (optional)

# After DNS propagates, remove Fly.io
fly apps destroy your-app-name

Real Savings: Before and After

Here's what a typical Fly.io setup costs vs RAW:

ComponentFly.io CostRAW Cost
App server (2 CPU / 2 GB)$29/mo$11/mo (everything included)
Postgres Machine$29/mo
Redis Machine$15/mo
IPv4 address$2/mo
Bandwidth (500 GB)$8/mo
Total$83/mo$11/mo

RAW raw-4 ARM: 4 vCPU, 8 GB RAM, 80 GB NVMe, 20 TB bandwidth. Run app + Postgres + Redis on one server.

That's $864/year saved on a modest setup. For teams running multiple services, the savings scale linearly.

When to Stay on Fly.io

  • You need true multi-region deployment (5+ regions)
  • You're building real-time apps where sub-30ms global latency matters
  • You need Fly.io's Anycast networking specifically
  • Your app is tiny and fits in the free tier permanently

For the other 90% of apps: APIs, web apps, SaaS products, internal tools, background workers — a single bare metal server is faster, simpler, and dramatically cheaper.

Deploy on RAW in 13 Seconds

No cold starts. No surprise bills. No usage meters. Just a dedicated server that's always on, always fast, and costs less than a single Fly.io Machine.

$ npx rawhq deployDeploy Free Server →