Fly.io Provider

Deploy preview environments to Fly.io using the Machines REST API.

How it works

Everything runs in your own Fly account — Previewops does not use its own infrastructure for builds or storage.

Prerequisites

  1. A Fly.io account and organisation.
  2. Your Fly organisation must support privileged machines — required for the Docker builder. Most Fly orgs have this by default. If you see a permission denied error on the first build, contact Fly support to enable it.

Step 1 — Create a Fly API token

fly tokens create deploy --name previewops

Copy the token — you will need it in Step 3.

Step 2 — Find your org slug

fly orgs list

The slug is in the Slug column (e.g. personal or my-company).

Step 3 — Store credentials in Previewops

Click Credentials in the left sidebar and add the following keys under the Fly.io provider:

Key Required Value
FLY_API_TOKEN Token from Step 1
FLY_ORG_SLUG Org slug from Step 2 (e.g. my-fly-org)
FLY_REGION Fly region code (default: iad). Run fly platform regions to list options.

Once stored, you can comment /deploy-previewops --provider=fly on any PR — no .previewops.yaml file is required.

Step 4 — Configure the repo (optional)

If you need per-repo overrides, add .previewops.yaml to the repo root:

provider: fly
providerConfig:
  orgSlug: my-fly-org   # overrides FLY_ORG_SLUG credential
  region: iad           # overrides FLY_REGION credential

Values in .previewops.yaml always take precedence over the credentials stored in Step 3.

Complete example with all options:

# .previewops.yaml
provider: fly
providerConfig:
  orgSlug: my-fly-org    # Fly org slug (optional if FLY_ORG_SLUG stored as credential)
  region: iad            # Fly region (default: iad); run `fly platform regions` to list options

# Common options — all optional
concurrency: 3           # max simultaneous active previews per repo
ttlHours: 24             # auto-delete after N hours (default: 24)
port: 3000               # optional — auto-detected from your Dockerfile EXPOSE; falls back to 8080
memory: 512Mi            # container memory — e.g. 256Mi, 512Mi, 1Gi (default: 512Mi)
cpu: 1                   # container vCPU (default: 1)
dockerfile: Dockerfile   # Dockerfile path relative to repo root (default: Dockerfile)
buildContext: .          # Docker build context (default: .)
env:
  NODE_ENV: preview      # inject env vars into the preview container

Step 5 — Verify

Comment /validate-previewops on any open PR. The bot will confirm your token is valid and your org is reachable.

Build times

Scenario Typical duration
First build (no cached layers) 5–15 minutes
Subsequent builds (dependencies unchanged) 2–5 minutes

The main variable is your Dockerfile — specifically how long docker build takes for your dependencies.

Tip: structure your Dockerfile so dependency installation (COPY package.json && npm install) comes before copying your source files (COPY . .). Docker caches each layer, so unchanged dependencies won't be reinstalled on subsequent builds.

Build progress updates

Because Fly builds are slower than other providers, Previewops posts intermediate progress updates to the PR comment so you can track where the build is:

Message What's happening
🏗️ Provisioning Fly builder machine… Creating the remote Docker builder VM in your Fly org
⚙️ Builder machine ready — waiting for exec agent… Builder VM is up, waiting for the exec agent inside the machine to become ready
🐳 Transferring build script… Exec agent ready, uploading the build script to the builder VM
🔨 Docker build in progress… (Ns) Build is running — elapsed time shown, updated every 15 seconds
🚀 Image ready — deploying preview machine… Build complete, creating your preview machine
⏳ Machine created — waiting for service to become reachable… Machine is booting and starting your app

Dockerfile requirements

Notes

Troubleshooting

Error Cause Fix
FLY_API_TOKEN environment variable is required Token not stored Add FLY_API_TOKEN in Credentials (left sidebar)
fly orgSlug is required Org slug missing Add FLY_ORG_SLUG credential or orgSlug in .previewops.yaml
401 Unauthorized from Fly API Token expired or invalid Regenerate: fly tokens create deploy --name previewops
permission denied on first build Privileged machines not enabled for your org Contact Fly support
IP allocation failed — no shared_v4 found Transient Fly API timing issue Retry the deploy — this resolves on its own
Your application crashed immediately after startup App process exited at boot See App crashes on startup
Build timed out after 3600s Build exceeded 1 hour Check your Dockerfile for unusually slow steps; ensure the base image is accessible
Preview machine did not reach 'started' state Machine start timed out (60s) Retry; if persistent, check your Fly org's machine quotas
Build failed with empty package directories Build environment compatibility issue See Build fails with empty package directories
Build marked ❌ Failed on first attempt with no clear error Transient setup failure on first deploy or after extended inactivity See Build fails on first attempt
Deploy timed out with no clear error in the PR comment App crashed on startup or isn't listening on the expected port Confirm your app listens on port 8080 (or the port: value in .previewops.yaml) and doesn't exit at boot. Run docker build && docker run -p 8080:8080 locally to reproduce.

Build fails on first attempt

Occasionally a build will fail on the first /deploy-previewops --provider=fly attempt even when your Dockerfile and credentials are correct. This can happen on initial setup or after extended inactivity.

Simply retry the command. The second attempt should complete successfully.

If the retry also fails, check the build output for a specific error message and consult the table above.

Build fails with empty package directories

If your build fails and your package manager appears to have run but produced no output files, ensure your Dockerfile uses standard copy operations without requiring special filesystem capabilities. Previewops automatically selects the most compatible build mode for Fly — no configuration change is needed on your side.

If the issue persists, try adding a RUN echo "build ok" after your install step to confirm which layer is failing, then share the build output with Previewops support.

App crashes on startup

When the bot posts "Your application crashed immediately after startup", the preview machine started successfully but your app process exited before it could accept connections.

Common causes by exit code:

Exit code Likely cause
1 Unhandled exception or startup error — check your application logs
127 Command not found — a dependency or binary is missing from the image
137 Out of memory — increase memory in .previewops.yaml
? Very fast crash before the exit code was recorded

Debugging steps:

  1. Confirm your app starts locally with Docker:
    docker build -t test . && docker run -e PORT=8080 -p 8080:8080 test
    
  2. Confirm your app reads PORT from the environment and listens on that port. Previewops sets PORT automatically.
  3. If exit code 137 (OOM), increase memory in .previewops.yaml:
    memory: 1Gi   # default is 256Mi
    
  4. Check your Dockerfile CMD/ENTRYPOINT — ensure it starts the server and does not exit immediately.

Token permissions

The deploy token scope (created via fly tokens create deploy) is sufficient for all Previewops operations. You do not need an admin or org-owner token.

If you rotate your Fly API token, update it in Credentials (left sidebar) and re-run /validate-previewops to confirm the new token works before triggering a deploy.