TL;DR: Fly.io is a platform that runs your full-stack app on servers around the world — not just static files, but actual backend servers with databases, persistent storage, and long-running processes. You give it your code, it packages it into a container, and deploys it to data centers close to your users. Think of it as the step up from Vercel or Netlify when your app outgrows serverless hosting. The main commands are fly launch to set up, fly deploy to ship, and fly logs to debug.

Why AI Coders Need to Know This

There is a moment in every AI coder's journey where deployment gets real. You asked Claude or Cursor to build you an app — maybe a project management tool, a SaaS dashboard, or a marketplace. It runs perfectly on your laptop. Then you try to deploy it.

If your app is a static site or a Next.js frontend with a few API routes, platforms like Vercel and Netlify handle it beautifully. But the moment your app needs a real backend — a Node.js or Python server that stays running, a PostgreSQL database, WebSocket connections, background jobs, file uploads that persist — those platforms either cannot do it or make it painfully complicated.

That is where Fly.io comes in. It runs actual servers. Your app does not get squeezed into a serverless function with a 10-second timeout. It runs as a real process on a real machine, with a real filesystem and a real network connection. And it can run in multiple regions around the world, so your users in Tokyo and London get fast responses too.

If you are building with AI and your projects are growing beyond simple frontends, you need to understand what Fly.io does and how to use it. Your AI will suggest it. This guide makes sure you understand what it is suggesting.

The Global Workshop Analogy

Imagine you build custom furniture. Platforms like Vercel and Netlify are like renting a display window — great for showing off your catalog (a static website), but you cannot actually build anything in the window. There is no workbench, no tools, no storage room.

Fly.io is like renting a full workshop. You have a workbench (a server), tool storage (persistent volumes), a locked cabinet for valuable materials (secrets management), and even a dedicated room for your records (a database). You can build things, store things, and run equipment that stays on all day.

But here is the clever part: Fly.io does not just give you one workshop in one city. It can set up copies of your workshop in cities around the world. A customer in Sydney walks into the Sydney workshop. A customer in Frankfurt walks into the Frankfurt workshop. They all get the same product, but faster, because the workshop is right there.

That is what Fly.io does with your app. It runs your server code inside containers deployed to data centers in the regions you choose. Your users connect to the nearest one automatically.

Real Scenario

You asked AI to build a task management app. It created a Node.js backend with Express, a PostgreSQL database for storing tasks, user authentication with sessions, and a React frontend. Everything works on localhost:3000.

You try Vercel first because you have heard of it. The frontend deploys fine, but the Express backend does not work — Vercel wants serverless functions, not a long-running Express server. Your database needs somewhere to live. Your session store needs persistent memory.

You ask AI for help:

Prompt I Would Type

I built a Node.js Express app with a PostgreSQL database and session
auth. It works locally. Vercel can't run the Express server.

I need to deploy this somewhere that can run a real backend with a
database. Walk me through deploying this on Fly.io step by step.
Explain every command and config option — I've never used it before.

AI responds with a deployment plan. Here is what it generates and what every piece actually means.

What AI Generated

First, AI tells you to install the Fly CLI and log in:

# Install the Fly.io command-line tool
# macOS (with Homebrew):
brew install flyctl

# Or on any system:
curl -L https://fly.io/install.sh | sh

# Log in to your Fly.io account (opens browser)
fly auth login

Then it tells you to initialize your app:

# Run this inside your project directory
# fly launch scans your code, detects it is a Node.js app,
# generates a Dockerfile and fly.toml config file
fly launch

The fly launch command is doing a lot behind the scenes. It detects your app's language and framework, generates a Dockerfile if you do not have one, creates a fly.toml configuration file, picks a region for your first deployment, and registers your app with Fly.io's platform. You will answer a few interactive questions — app name, region, whether you want a database.

Here is the fly.toml file it generates — the central config for your Fly app:

# fly.toml — your app's deployment configuration
# This file tells Fly.io how to run your app

app = 'my-task-app'              # Your app's unique name on Fly.io
primary_region = 'sea'           # Primary deployment region (Seattle)

[build]
  # Fly uses the Dockerfile it generated to build your app image
  # You can customize the Dockerfile if needed

[env]
  PORT = '8080'                  # The port your app listens on inside the container
  NODE_ENV = 'production'        # Tell Node.js this is production

[http_service]
  internal_port = 8080           # Port inside the container (must match PORT above)
  force_https = true             # Redirect all HTTP traffic to HTTPS
  auto_stop_machines = 'stop'    # Stop the machine when no traffic (saves money)
  auto_start_machines = true     # Automatically restart when a request comes in
  min_machines_running = 0       # Allow scaling down to zero machines
  processes = ['app']            # Which process group this applies to

[[vm]]
  memory = '256mb'               # RAM allocated to each machine
  cpu_kind = 'shared'            # Shared CPU (cheapest option)
  cpus = 1                       # Number of CPU cores

Then AI tells you to set up the database and deploy:

# Create a managed PostgreSQL database on Fly
# This sets up a separate Fly app running Postgres
fly postgres create --name my-task-app-db

# Attach the database to your app
# This automatically sets the DATABASE_URL secret on your app
fly postgres attach my-task-app-db

# Set any other secrets your app needs (like session keys)
# These become environment variables inside your running app
fly secrets set SESSION_SECRET="your-random-secret-string-here"

# Deploy your app — builds the Docker image and ships it
fly deploy

After fly deploy finishes, your app is live at https://my-task-app.fly.dev. That is a real server running your Express app, connected to a real PostgreSQL database, accessible from anywhere in the world.

Understanding Each Part

Machines — your app's servers

Fly.io runs your app inside "Machines" — lightweight virtual machines that start in about 300 milliseconds. Each Machine runs one instance of your app. You can have one Machine or dozens, spread across different regions. Unlike traditional servers you rent by the month, Fly Machines can stop when nobody is using your app and start instantly when a request comes in. You only pay for the time they are running.

Regions — where your app runs

Fly.io has data centers in 30+ cities worldwide. When you run fly launch, you pick a primary region — like sea (Seattle), iad (Virginia), lhr (London), or nrt (Tokyo). Your app runs there by default. You can add more regions later with fly scale count 2 --region sea,lhr to run Machines in both Seattle and London. Users connect to the nearest one automatically. For most vibe coders starting out, one region is plenty.

Volumes — persistent storage that survives restarts

By default, when a Fly Machine stops or restarts, any files your app wrote to the filesystem are gone. This catches a lot of people off guard — especially if your app uses SQLite or saves uploaded files to disk. Volumes solve this. A volume is a persistent disk that gets mounted into your Machine, like plugging in an external hard drive. Data on a volume survives restarts and redeployments.

# Create a 1GB volume in Seattle
fly volumes create myapp_data --region sea --size 1

# Add to fly.toml so it mounts automatically
[mounts]
  source = 'myapp_data'
  destination = '/data'     # Your app reads/writes files here

Important limitation: a volume is tied to one region and one Machine. If you run Machines in multiple regions, each needs its own volume. Volumes are not shared storage — they are like a local hard drive for one specific Machine.

Secrets — your app's private configuration

Secrets are environment variables that contain sensitive data — database passwords, API keys, session secrets. You set them with fly secrets set and they become available as environment variables inside your running app. They are encrypted at rest and never written to your fly.toml file or your code.

# Set secrets (each one restarts your app)
fly secrets set DATABASE_URL="postgres://..." SESSION_SECRET="abc123"

# List secrets (values are hidden)
fly secrets list

# Remove a secret
fly secrets unset OLD_API_KEY

Every time you set or change a secret, Fly redeploys your app. This is by design — the new environment variable needs to be loaded into a fresh process.

Postgres on Fly — your managed database

Fly offers managed PostgreSQL that runs as a separate Fly app in your account. When you run fly postgres create, it spins up a Postgres cluster with automatic failover and backups. When you fly postgres attach, it sets the DATABASE_URL secret on your main app automatically, so your code can connect to the database without any manual configuration.

This is not the same as a fully managed database service like AWS RDS or Supabase — you are responsible for monitoring and maintenance. But it is dramatically easier than setting up Postgres from scratch on a VPS, and for most vibe coder projects, it works great.

Auto-stop and auto-start — saving money while you sleep

The auto_stop_machines and auto_start_machines settings in fly.toml are what make Fly affordable for small projects. When nobody is using your app, the Machine stops and you stop paying for compute. When a request comes in, Fly starts a Machine in about 300 milliseconds and routes the request to it. The first user after a quiet period gets a slightly slower response (the "cold start"), but everyone after that gets normal speed.

If cold starts are a problem — say you are running a real-time chat app — set min_machines_running = 1 to keep one Machine always on. This costs more but eliminates the cold start delay.

Fly.io vs Vercel vs Railway vs Render

Every platform has a sweet spot. Here is what each one is actually best for as a vibe coder:

Platform Best For Runs Real Servers Built-in Database Global Regions Learning Curve
Fly.io Full-stack apps, APIs, WebSockets, global deployment ✅ Yes ✅ Managed Postgres ✅ 30+ regions Medium
Vercel Next.js, React, static sites, frontend-heavy apps ❌ Serverless only ⚠️ Vercel Postgres (limited) ✅ Edge network Low
Railway Quick backend deploys, hobby projects, simple full-stack ✅ Yes ✅ One-click Postgres, MySQL, Redis ⚠️ Limited regions Low
Render Heroku replacement, straightforward backend hosting ✅ Yes ✅ Managed Postgres ⚠️ Limited regions Low

The quick decision guide: If your app is mostly a frontend with a few API routes, use Vercel. If you want the easiest possible backend deploy and do not care about global regions, use Railway. If you need real servers running close to users around the world with fine-grained control, use Fly.io. If you want a straightforward Heroku-like experience, use Render.

What AI Gets Wrong About Fly.io

Forgetting to set secrets

This is the number one Fly.io deployment failure. AI generates your app code with process.env.DATABASE_URL and process.env.SESSION_SECRET, writes a Dockerfile, tells you to run fly deploy — and then your app crashes because those environment variables do not exist on Fly. AI often forgets that you need to explicitly set every secret with fly secrets set before deploying. If your app crashes immediately after deploy, this is the first thing to check.

# Check what secrets are set on your app
fly secrets list

# You'll see something like:
# NAME            DIGEST                  CREATED AT
# DATABASE_URL    a1b2c3d4e5              2h ago
#
# If a secret your app needs is missing — that's your problem

Not configuring volumes for persistent data

AI will happily generate code that writes files to disk — SQLite databases, uploaded images, log files — without telling you that Fly Machines have ephemeral storage. Your app deploys fine, works for a while, then loses all its data on the next restart or deploy. If your app stores anything to the filesystem, you need a volume. And your code needs to write to the mounted path (like /data), not just anywhere on the filesystem.

Assuming free tier limits are more generous than they are

AI sometimes tells you Fly.io is "free" without mentioning the limits. The free allowance covers 3 shared-CPU VMs with 256MB RAM each. A managed Postgres database uses one or two of those VMs. So if you create a Postgres database and deploy your app, you might already be at 3 VMs — meaning any additional services will cost money. Always run fly scale show and fly apps list to see what you are actually running.

Generating outdated deployment commands

Fly.io has evolved rapidly. AI trained on older data might suggest flyctl instead of fly, reference the older "Apps V1" platform instead of Machines, or use deprecated flags. If a command does not work, check the official Fly.io docs — they are genuinely well-written and up to date.

Ignoring the Dockerfile

When fly launch generates a Dockerfile, AI will sometimes tell you "you do not need to worry about it." But the Dockerfile is where build errors happen — missing system dependencies, wrong Node.js version, forgotten build steps. If fly deploy fails during the build phase, the Dockerfile is almost always where the problem lives. Ask your AI to explain the generated Dockerfile line by line so you know where to look when something breaks.

The Golden Rule of Fly.io Deployment

If your app works locally but crashes on Fly, check three things in this order: (1) Are all secrets set? Run fly secrets list. (2) Is your app listening on the right port? It must match internal_port in fly.toml. (3) Does your app need persistent storage? If it writes to disk, you need a volume. These three cover 90% of first-deploy failures.

Security Considerations

Deploying to Fly.io means your app is on the public internet. Here are the security basics that AI rarely brings up on its own.

Secrets management — never hardcode credentials

Never put database passwords, API keys, or session secrets in your code, your fly.toml, or your Dockerfile. Use fly secrets set for every sensitive value. These are encrypted at rest and only available to your running app as environment variables. If your AI generates code with a hardcoded DATABASE_URL or API_KEY string, move it to a secret immediately.

Network access — your app is public by default

When you deploy to Fly, your app gets a public URL (your-app.fly.dev) and is accessible to the entire internet. Your database, however, is on a private network by default and only accessible from your other Fly apps. Do not change this. Do not expose your database port publicly. If you need to access the database directly for debugging, use fly proxy 5432 -a my-task-app-db to create a secure tunnel from your local machine.

HTTPS is automatic — but verify it

Fly.io provides free TLS certificates and handles HTTPS automatically when you set force_https = true in fly.toml. This is already in the default config, but it is worth double-checking. Your users should never access your app over plain HTTP.

SSH access — a powerful debugging tool

Fly lets you SSH into your running Machine with fly ssh console. This is incredibly useful for debugging, but it also means anyone with access to your Fly account can access your server's filesystem. Use strong passwords, enable two-factor authentication on your Fly.io account, and be careful about who has access to your fly CLI session.

How to Debug With AI

"fly deploy" failed — build errors

If deployment fails during the build phase, the problem is in your Dockerfile. Common causes: wrong Node.js version, missing system dependencies (like python3 or build-essential for native modules), or a build script that fails silently. Copy the full error output and paste it to your AI with this prompt:

Debug Prompt

My fly deploy failed during the build step. Here is the full error:

[paste error output]

Here is my Dockerfile:

[paste Dockerfile]

What is wrong and how do I fix it? Explain what each line of the
Dockerfile does so I can understand where it broke.

"App deployed but crashes immediately"

Your app built successfully but is not running. First, check the logs:

# Show the most recent logs from your running (or crashing) app
fly logs

# Check the status of your app's Machines
fly status

# Common things you'll see in logs when something is wrong:
# - "Error: connect ECONNREFUSED" → database connection failed (check secrets)
# - "Error: listen EADDRINUSE" → port conflict (check internal_port)
# - "MODULE_NOT_FOUND" → a dependency is missing from package.json

"App works but data disappears"

If your app loses data after a redeploy, you are writing to ephemeral storage instead of a volume. Check if you have a [mounts] section in fly.toml and make sure your app writes to the mounted path, not to a random directory:

# Check your volumes
fly volumes list

# SSH into the machine and verify the mount
fly ssh console
ls -la /data    # Should show your volume's contents

The "fly ssh console" trick

When all else fails, SSH into your running Machine and poke around. This is the equivalent of sitting down at the server and looking at what is actually happening:

# SSH into your running app
fly ssh console

# Once inside, you can:
printenv                    # See all environment variables (verify secrets)
ls -la /data                # Check volume contents
cat /app/fly.toml           # Verify your config
node -e "console.log(process.env.DATABASE_URL ? 'DB URL is set' : 'DB URL is MISSING')"

Useful Fly commands for debugging

# Live logs — watch in real time
fly logs

# App status — see which Machines are running and in what regions
fly status

# List all your Fly apps (including database apps)
fly apps list

# See how many resources you're using
fly scale show

# Open your app in the browser
fly apps open

# Restart your app (sometimes fixes things)
fly apps restart

What to Learn Next

Fly.io sits at the intersection of several infrastructure concepts. These are the most useful next reads for understanding the full picture:

  • What Is Docker? — Fly.io uses Docker under the hood to package and run your app. Understanding what a Dockerfile does will help you debug 90% of build failures.
  • What Is Vercel? — The platform most vibe coders start with. Understanding when Vercel is enough and when you need Fly.io saves you from over-engineering simple projects.
  • What Is Railway? — The easiest alternative to Fly.io for backend deployment. Great comparison point for deciding which platform fits your project.
  • What Is an Environment Variable? — Secrets on Fly.io are environment variables. This guide explains what they are, why they matter, and how your code reads them.
  • What Is a VPS? — If you want to understand what Fly.io is abstracting away for you, a VPS is the raw version — renting a server and managing everything yourself.
  • What Is a REST API? — Most apps you deploy to Fly.io are APIs. Understanding REST fundamentals helps you build and debug them.

Next Step

Take an app you have been running on localhost and deploy it to Fly.io. Run fly launch, let it auto-detect your stack, then fly deploy. When something breaks — and something will — use fly logs and the debug prompts above to fix it with AI. The first deploy is always the hardest. After that, shipping updates is just fly deploy.

FAQ

Fly.io has a free allowance that includes up to 3 shared-CPU VMs, 160GB of outbound transfer, and 3GB of persistent storage per month. That is enough to run a small app for testing or personal use. Once you exceed those limits, you pay per resource. You do need to add a credit card to sign up, even for the free tier.

Vercel is optimized for frontend frameworks like Next.js and handles static sites and serverless functions. Fly.io runs full backend servers — long-running processes, databases, WebSockets, background jobs. If your app is mostly a frontend with API routes, Vercel works great. If your app needs a persistent backend server or a database that lives next to your app, Fly.io is the better fit.

Not necessarily. When you run fly launch, Fly.io auto-detects your app type (Node.js, Python, Go, etc.) and generates a Dockerfile for you. You do not need to write Docker commands from scratch. However, understanding the basics of what a Dockerfile does will help you debug deployment issues when they come up. Your AI can explain the generated Dockerfile line by line.

Fly Machines use ephemeral storage by default — when a Machine stops or restarts, any files written to the filesystem are gone. If your app stores data locally (like SQLite databases or uploaded files), you need to attach a Fly Volume, which provides persistent disk storage that survives restarts. Add a [mounts] section to your fly.toml to configure this.

Yes. Fly.io offers managed Postgres through fly postgres create, which sets up a PostgreSQL cluster on Fly infrastructure. You can also run any database yourself using Fly Volumes for persistent storage — SQLite, MySQL, or MongoDB. The managed Postgres option is easier to set up but gives you less control. For most vibe coders, managed Postgres is the right starting point.