To migrate from Fly.io to Vercel, rename your Dockerfile to Dockerfile.vercel, make sure it serves HTTP on the port in the PORT environment variable, and push to a Vercel project. Vercel builds the image, stores it in Vercel Container Registry, and runs it as a function that scales with traffic. Configuration moves from fly.toml to environment variables and vercel.json, and stateful pieces such as Fly Volumes move to external backing services.
This guide maps each Fly.io concept to its Vercel equivalent, walks through the migration, and is direct about what doesn't carry over.
Copy link to headingShould your app move?
Vercel runs containers as stateless HTTP services. The fit test comes down to three questions:
- Does the app serve HTTP and keep no state between requests? Sessions, uploads, and caches must live in a backing service, not on local disk or in process memory.
- Does it tolerate scale-to-zero? Instances scale down after 5 minutes of inactivity in production. If you run Machines with
min_machines_runningabove zero because the app can't cold start, resolve that first. - Is it request-driven? Background workers, queue consumers, and cron processes that run outside the request cycle have no container equivalent on Vercel and should either be left behind or restructured.
If the app is a web service or API that happens to run in a container, it moves cleanly. If it depends on attached volumes or always-on processes, continue reading for what doesn't map before starting.
Copy link to headingHow Fly.io concepts map to Vercel
| Fly.io | Vercel |
|---|---|
Dockerfile | Dockerfile.vercel (or Containerfile.vercel) |
internal_port in fly.toml | PORT environment variable in project settings, default 80 |
fly deploy | git push, or vercel deploy from the CLI |
| Fly.io image registry | Vercel Container Registry at vcr.vercel.com |
Machines with [[vm]] sizing | Fluid compute, no instances to size |
auto_stop_machines / auto_start_machines | Automatic: scale-in after 5 minutes idle in production, 30 seconds in preview |
kill_signal / kill_timeout | SIGTERM with a 30-second grace period |
fly secrets set | Secret environment variables in project settings |
[env] in fly.toml | Environment variables in project settings |
| Fly Volumes | External storage: databases, caches, or blob storage |
| Multiple apps or process groups | Services in one project, routed with rewrites |
Copy link to headingSteps
Copy link to heading1. Prepare the Dockerfile
Copy your existing Dockerfile to Dockerfile.vercel at the project root. The image contents follow standard OCI conventions, so most Dockerfiles need no changes; what matters is the port contract. Vercel routes traffic to the port in the PORT environment variable, which defaults to 80. On Fly.io, the port lives in fly.toml as internal_port, and the app itself often hardcodes it.
If your server already reads PORT from the environment, as most Fly.io apps do, nothing changes. If it hardcodes a port, you have two options: update the code to read PORT, or set PORT in your Vercel project settings to the port the server listens on. Don't rely on an ENV PORT line in the Dockerfile to change where traffic routes; the routed port comes from project settings, not the image.
Copy link to heading2. Create the Vercel project and deploy
Import the repository into a new Vercel project. Vercel detects Dockerfile.vercel, builds the image on every commit, pushes it to Vercel Container Registry, and serves it on Fluid compute with Active CPU pricing, so you pay for the CPU your code uses rather than for provisioned Machines.
Every commit also gets a preview deployment with its own URL, replacing the staging Fly.io app pattern: instead of maintaining a second app for testing, every branch automatically gets an isolated environment. If your frontend already deploys on Vercel, the same PR previews it and the migrated backend together.
Copy link to heading3. Move configuration and secrets
Copy the [env] section of fly.toml into your Vercel project's environment variables, and recreate anything set with fly secrets set as Secret environment variables. Secret is the write-only credential type in the current Environment Variables UI: values can't be read back after you save them. Vercel injects both into the container at runtime the same way Fly.io does, so application code doesn't change.
fly.toml settings that configured the platform rather than the app, such as auto_stop_machines, force_https, and [[vm]] blocks, have no destination: scale-to-zero and HTTPS are automatic, and there are no instances to size.
Copy link to heading4. Move state out of the container
This is the step that decides the migration. On Fly.io, a Machine can mount a volume and keep state locally; on Vercel, each container instance takes a request, responds, and keeps nothing:
- Fly Postgres or a database on a Machine moves to a managed database, such as one from the Vercel Marketplace, and the connection string is set as an environment variable.
- Files on volumes move to blob storage.
- In-process caches and sessions are moved to a managed cache, such as Redis.
If the app already ran with external state on Fly.io, as many do, this step is only connection strings.
Copy link to heading5. Consolidate multiple apps into services
Where you ran separate Fly.io apps for a frontend and an API, or process groups in one app, define each as a service in vercel.json. Set each container-backed service's runtime to container and point its entrypoint at that service's Dockerfile, then route between services with rewrites. Vercel generates environment variables for each service, so they call each other without hardcoded URLs, and the whole project deploys to one domain and runs locally with a single command.
Copy link to heading6. Migrate images you want to keep
Deploys build fresh images from Dockerfile.vercel, so most teams don't need to move old images at all. If you keep release images for rollback or compliance, copy them into VCR with standard Docker tooling; the commands in the GHCR migration guide work against any OCI registry, including Fly.io's. Run fly auth docker first to authenticate Docker with Fly.io's registry, then docker buildx imagetools create copies multi-platform images without pulling layers locally.
Copy link to heading7. Verify and cut over
Run the app at its Vercel preview URL against production backing services on read-only paths first, then shift traffic by updating DNS to point to the Vercel deployment. Keep the Fly.io app running until Vercel serves production traffic cleanly, then scale Machines down and remove the app. Because the container also runs locally with vercel dev, using the Docker daemon on your machine, you can reproduce any behavior difference before cutover rather than after.
Copy link to headingWhat doesn't map from Fly.io
- Volumes. There is no attached persistent disk. If the app writes anything to local paths it expects to survive restarts, that data must move to external storage before migrating.
- Always-on processes. Background workers and queue consumers aren't request-driven, so they don't fit the container-as-function model. Keep them where they are, or restructure them to focus on request-triggered work.
release_command. There's no deploy-time hook for migrations. Run them from CI before the deploy, or from a separate step in your pipeline.- Long shutdown windows. Fly.io lets you set
kill_timeout; on Vercel the grace period afterSIGTERMis fixed at 30 seconds. If cleanup takes longer, shorten it. - Two networking features. Secure Compute and Static IPs aren't supported with custom container images yet. If you allowlist Fly.io egress IPs with an external service today, plan around that.
Copy link to headingFrequently asked questions
Copy link to headingCan I reuse my Fly.io Dockerfile on Vercel?
Yes, usually unchanged. Copy it to Dockerfile.vercel and confirm the server reads its port from the PORT environment variable. The image itself follows standard OCI conventions on both platforms.
Copy link to headingWhat replaces auto_stop_machines and auto_start_machines?
Nothing to configure. Vercel scales container instances down automatically after five minutes without traffic in production and back up when traffic returns. Images are stored as precompiled snapshots in Vercel Container Registry, which keeps cold starts fast.
Copy link to headingDoes Vercel have an equivalent of Fly Volumes?
No. Containers on Vercel are stateless, so persistent data lives in external services: a managed database, cache, or blob storage. Anything the app writes to local disk exists only for the life of that instance.
Copy link to headingHow does pricing differ from paying for Machines?
On Vercel you don't provision or size instances. Containers run as Vercel Functions with Active CPU pricing, so cost follows the CPU time your code uses, and idle time isn't billed as a running Machine.
Copy link to headingCan I migrate gradually?
Yes. Vercel preview deployments run the containerized app on real URLs before any DNS change, so you can test against production backing services, move read-only traffic first, and keep the Fly.io app running until you've verified the cutover.
Copy link to headingRelated resources
- Migrate to Vercel from Railway, Render, or Google Cloud Run
- Does Vercel support Docker deployments? and How to migrate from GHCR to Vercel Container Registry guides
- Run any Dockerfile on Vercel on the Vercel blog
- Container images and Vercel Container Registry in the Vercel Docs
- 7 ways to use Docker containers on Vercel