Fly.io Provider
Deploy preview environments to Fly.io using the Machines REST API.
How it works
- Build: Previewops starts a Docker builder machine inside your own Fly org, clones your repository, runs
docker build, and pushes the image to your Fly registry. The builder machine is removed after each build. - Deploy: creates a Fly app and Fly Machine running your image. The machine's
.fly.devhostname is posted as the preview URL. - Delete: stops and deletes the Fly Machine and app when the PR is closed.
- List: identifies active preview apps in your org by metadata tags written at deploy time.
Everything runs in your own Fly account — Previewops does not use its own infrastructure for builds or storage.
Prerequisites
- A Fly.io account and organisation.
- Your Fly organisation must support privileged machines — required for the Docker builder. Most Fly orgs have this by default. If you see a
permission deniederror 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
Navigate to Dashboard → Credentials 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.
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. Build progress is polled every 15 seconds, so the reported duration may be rounded up slightly.
Tip: structure your
Dockerfileso 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.
Dockerfile requirements
- Your app must listen on the port configured in
.previewops.yaml(default:3000). Fly routes HTTPS traffic to this port automatically. - Your
Dockerfilemust produce a runnable image —CMDorENTRYPOINTmust start the server process. - Standard
docker buildsyntax is fully supported.
Notes
- Each PR gets its own Fly app (
prev-{owner}-{repo}-pr{number}, max 30 characters). - Images are stored in your Fly registry (
registry.fly.io) — no external registry needed. - Preview machines use
auto_start_config— they wake on the first inbound HTTPS request and sleep after 5 minutes of inactivity, keeping costs near zero. - Fly's free allowance covers 3 shared-cpu 256 MB machines and 160 GB outbound transfer/month.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
FLY_API_TOKEN environment variable is required |
Token not stored | Add FLY_API_TOKEN in Dashboard → Credentials |
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 | Build infrastructure was interrupted mid-run | See Build fails on first attempt |
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 when the remote build environment is interrupted before the build completes — typically on the very first deploy to a new Fly org or after a long period of inactivity.
Simply retry the command. The second attempt provisions fresh build infrastructure and should complete successfully.
If builds fail consistently across multiple retries, 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:
- Confirm your app starts locally with Docker:
docker build -t test . && docker run -e PORT=3000 -p 3000:3000 test - Confirm your app reads
PORTfrom the environment and listens on that port. Previewops setsPORTautomatically. - If exit code 137 (OOM), increase memory in
.previewops.yaml:memory: 1Gi # default is 256Mi - Check your
DockerfileCMD/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 Dashboard → Credentials and re-run /validate-previewops to confirm the new token works before triggering a deploy.
Prerequisites
- A Fly.io account.
- A Fly organisation (personal accounts use your username as the org slug).
- The org must support privileged machines — required for the Docker-in-Docker builder. Verify with
fly platform vm-sizesor by contacting Fly support if you hit a restriction.
Step 1 — Create a Fly API token
fly tokens create deploy --name previewops
Store the output as FLY_API_TOKEN in Previewops secrets.
Step 2 — Find your org slug
fly orgs list
The slug is the value in the Slug column (e.g. personal or my-company).
Step 3 — Store secrets in Previewops
Navigate to Dashboard → Credentials and add the following keys for 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.io region code (default: iad) — see fly platform regions |
Once all three keys are stored, you can simplify or eliminate your .previewops.yaml:
- No YAML at all: comment
/deploy-previewops --provider=fly— orgSlug and region are read from credentials. - Minimal YAML: add just
provider: fly(noproviderConfigneeded — orgSlug comes fromFLY_ORG_SLUGcredential).
Step 4 — Configure the repo (optional)
If you need per-repo overrides (e.g. a different org or region per repo), add a .previewops.yaml to the repo root. Values here take precedence over the credential keys stored in step 3.
provider: fly
providerConfig:
orgSlug: my-fly-org # overrides FLY_ORG_SLUG credential
region: iad # overrides FLY_REGION credential
If all values are stored as credentials (step 3), this file is optional.
Step 5 — Verify
Comment /validate-previewops on any open PR. The bot calls the Fly API and confirms the token is valid and the org exists.
Build times
| Build | Typical duration |
|---|---|
| First build (cold cache) | 5–10 minutes |
| Subsequent builds (warm cache) | ~1–2 minutes |
The builder machine is stopped (not deleted) after each build, so Docker layer cache is preserved. The main variables are your app's docker build time and how long docker push takes over Fly's network. PreviewOps polls for build completion every 15 seconds, so the total time reported may be rounded up to the next poll interval.
Notes
- Each PR gets its own Fly app (
prev-{owner}-{repo}-pr{number}). - A single builder machine (
previewops-build-{org}) is created once in your org and reused for all builds — it is stopped (not deleted) after each build to preserve the Docker layer cache. - Images are stored in your Fly registry (
registry.fly.io) — no GCP or external registry needed. - Fly Machines bill per-second (CPU + memory) — cost is near zero for short-lived previews.
- The preview URL is the app's
.fly.devhostname served over HTTPS — no extra setup needed. - Fly has a free allowance of 3 shared-cpu-1x 256 MB machines and 160 GB outbound transfer/month.
Troubleshooting
| Error | Fix |
|---|---|
FLY_API_TOKEN environment variable is required |
Set the token in Previewops credentials |
fly orgSlug is required |
Add FLY_ORG_SLUG credential or orgSlug to providerConfig in .previewops.yaml |
401 Unauthorized from Fly API |
Token expired — regenerate with fly tokens create deploy |
Build fails with permission denied |
The Fly org may not allow privileged machines — contact Fly support |