TL;DR: Run fly auth login once, run fly launch to scaffold a fly.toml in your project, then fly deploy ships it. Fly.io places your Docker container in 30+ regions automatically — pay per-CPU-second.
Fly.io is what happens when a Docker container can live in 30 cities at once. You bring an image; fly deploy places it near your users. Less work than a VPS, more control than a typical PaaS — and the only mainstream option that makes "global by default" cheap.
The traditional cloud picks one trade. Heroku/Render/Railway hide the infrastructure: simple to deploy, but you don't pick what runs where. A raw VPS at DigitalOcean exposes everything: you pick the machine and the city, but you're now responsible for OS, processes, restarts, scaling. Either model is fine for plenty of projects, but neither is quite right for "I want my app close to users in Sydney, São Paulo, and Frankfurt without learning Kubernetes."
Fly.io's model is "containers as primitives." You give them a Docker image (or a Dockerfile they'll build for you). They run it on lightweight Firecracker VMs they call "Machines," in any of 30+ regions, billable by the second. You pick which regions to deploy in; Fly's fly-replay header lets a request hit one region and bounce to another if needed. Apps that would have been one box in one city become "a small process per region the moment a user shows up there."
The catch is that this is more conceptual machinery than most PaaS. You write a fly.toml. You think about Machine sizes and scaling rules. You configure volumes if you need state. The pricing is per-second compute plus storage plus bandwidth — predictable but with more variables than a flat monthly Render plan. For projects that benefit from global reach or container-level control, the trade is worth it. For "I want to deploy a Next.js site and stop thinking," Vercel is simpler.
fly launch flowbrew install flyctl
# or:
curl -L https://fly.io/install.sh | sh
Then sign up + auth in one command:
fly auth signup # or: fly auth login if you have an account
A browser window opens for the OAuth flow. Once authorized, your local CLI holds a token. Fly requires a payment method on file before deploying anything (even on their generous free-credit tier) — add one in the dashboard.
The unit of deployment on Fly is a container image. You can:
ghcr.io/you/myapp:latest or similar.fly launch can detect common stacks (Node, Python, Go, Rails) and generate a Dockerfile.For most projects, write a Dockerfile alongside your code. If the app runs with docker run my-image locally on the right port, Fly can deploy it.
From your project root:
fly launch
The wizard asks: app name (must be globally unique), primary region (pick closest to you for dev), org (defaults to your personal one), and whether you want a Postgres or Redis instance alongside. For a first app, decline the database extras; you can add them later.
Fly creates a fly.toml in your project, builds an image (if needed), and offers to deploy. Say yes. A minute later, your app is live at https://<app-name>.fly.dev.
fly.toml file is your deployment config: app name, primary region, build settings, env vars, services and their ports, scaling parameters. It's checked into git; teammates running fly deploy get the same config you did.
Subsequent deploys are one command:
fly deploy
This rebuilds your image (or pushes a fresh one if you specified a remote image), creates new Machines with the new image, swaps traffic to them, kills the old Machines. Default deploy strategy is "rolling" — zero downtime for stateless apps.
Watch deploys with fly status and stream logs with fly logs. For app shells, fly ssh console opens a shell on a running Machine.
Your initial deploy put one Machine in your primary region. Add more:
fly scale count 3 --region iad,fra,syd
# 3 machines, one each in Ashburn / Frankfurt / Sydney
Fly's anycast routing sends each user to the nearest healthy Machine. For stateless apps (most web servers, most APIs), this works out of the box. For stateful apps, you need to think about which region has the writable database — Fly's fly-replay header is the canonical answer.
fly certs create mydomain.com
Fly shows the DNS records you need to add — usually an A or AAAA pointing at a Fly anycast IP, plus an _acme-challenge CNAME for validation. Add them at your DNS provider. Within a few minutes, Fly validates the domain, issues a Let's Encrypt cert, and your app is reachable at the custom name with HTTPS.
fly.toml. Fly's complexity is only worth it when you actually use the features.