How to Integrate Fly.io with Astro: Complete Guide
Step-by-step guide to integrating Fly.io with your Astro website.
Fly.io runs your applications on servers close to your users. Instead of picking one region and hoping for the best, Fly.io deploys your app to multiple locations worldwide and routes requests to the nearest one. For Astro sites that render on demand (server-side rendering), this means faster response times for visitors regardless of where they are.
Unlike traditional platform-as-a-service providers that abstract away the infrastructure, Fly.io gives you lightweight VMs called Machines. You get the simplicity of a managed platform with the control of real servers. Astro's rendering model pairs well with this because you can serve prerendered static pages from cache while running on-demand routes on Fly Machines.
Versions checked on 2026-05-29. This guide targets Astro 6.4.2 and the
@astrojs/nodeadapter 10.1.2. Note that Astro is now on version 6, and several older patterns changed. The biggest one:output: 'hybrid'was removed in Astro 5. Static is the default, and you opt individual routes into on-demand rendering by adding an adapter. Details below.
What Changed in Astro 5 and 6 (Read This First)
If you are following an older Fly.io plus Astro tutorial, watch for these breaking changes:
output: 'hybrid'no longer exists. In Astro 5 the'hybrid'and'static'build outputs were merged into a single'static'mode (the default). You no longer setoutput: 'hybrid'. Remove it if you have it. See the Astro v5 upgrade guide.- Static is the default, adapters unlock on-demand rendering. By default every page, route, and endpoint is prerendered at build time. To render a route at request time on the server, add an adapter and set
export const prerender = falseon that route (or setoutput: 'server'to flip the default for the whole project). See On-demand rendering. - Node requirement moved up. Astro 6 requires Node
>=22.12.0(per theastropackageenginesfield on npm). The old "Node 18+" advice no longer applies, and your Dockerfile base image needs to match.
Prerequisites
- Node.js 22.12.0 or newer (required by Astro 6)
- An Astro project (
npm create astro@latest) - A Fly.io account
- The Fly CLI (
flyctl) installed
Install the Fly CLI. These are the commands from the official Install flyctl page:
# macOS (Homebrew tap recommended by Fly; the core formula also works)
brew install superfly/tap/flyctl
# Linux
curl -L https://fly.io/install.sh | sh
# Windows (PowerShell)
powershell -Command "iwr https://fly.io/install.ps1 -useb | iex"
Confirm it installed, then log in:
flyctl version
flyctl auth login
Setting Up Astro for On-Demand Rendering
Fly.io runs your Astro app as a Node server, so install the Node.js adapter. The official approach is the astro add command, which installs @astrojs/node and edits your astro.config.* for you in one step:
npx astro add node
If you prefer to wire it up by hand, install the package and edit the config yourself:
npm install @astrojs/node
Update your astro.config.mjs:
import { defineConfig } from "astro/config";
import node from "@astrojs/node";
export default defineConfig({
// Static is the default. Add the adapter to enable on-demand rendering.
// Set output: "server" only if you want EVERY route rendered on demand.
// Otherwise leave output unset and mark individual routes with
// `export const prerender = false`.
output: "server",
adapter: node({
mode: "standalone",
}),
});
The standalone mode produces a self-contained server that starts itself when the entry module runs, which is exactly what Fly.io needs. The other mode, middleware, is for embedding Astro inside an existing Express or Fastify server. See the @astrojs/node docs.
In standalone mode the server reads two environment variables at runtime, HOST and PORT. This matters for Docker (more on that next).
Letting Fly Generate the Dockerfile (Easiest Path)
If you already added the Node adapter, the simplest path is to let Fly do the work. When you run flyctl launch, Fly detects the @astrojs/node adapter and, if no Dockerfile exists, generates one with the correct start command and environment variables, along with a fly.toml. The official Deploy your Astro Site to Fly.io guide and Fly's Astro framework docs both describe this auto-detection.
flyctl launch
If you want full control instead, write your own Dockerfile as shown below. A hand-written Dockerfile wins over the generated one.
Writing Your Own Dockerfile (Full Control)
Create a Dockerfile in your project root. Note the Node 22 base image to match Astro 6's engine requirement:
FROM node:22-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-slim AS runtime
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/package.json ./
# HOST=0.0.0.0 is REQUIRED inside Docker. Without it the standalone
# server binds to 127.0.0.1 and Fly's proxy cannot reach it.
ENV HOST=0.0.0.0
ENV PORT=8080
EXPOSE 8080
CMD ["node", "./dist/server/entry.mjs"]
Add a .dockerignore to keep the image small:
node_modules
.git
.env
dist
Launching on Fly.io
Initialize your Fly app:
flyctl launch
This creates a fly.toml configuration file. Edit it to match your setup:
app = "your-astro-app"
primary_region = "iad"
[build]
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = "stop"
auto_start_machines = true
min_machines_running = 0
[env]
NODE_ENV = "production"
Make sure internal_port matches the PORT your server listens on (8080 in the Dockerfile above). The auto_stop_machines and auto_start_machines settings keep costs low. Machines spin down when idle and start back up when requests come in.
Deploying
Deploy your app:
flyctl deploy
Fly.io builds the Docker image, pushes it to their registry, and starts your Machines. The first deploy takes a few minutes. Subsequent deploys are faster because Docker layers are cached.
Check your app status:
flyctl status
Open it in the browser:
flyctl open
Environment Variables
Set secrets for your app (these are encrypted and available as environment variables):
flyctl secrets set ADMIN_EMAIL=admin@example.com
flyctl secrets set ADMIN_PASS=your-secure-password
flyctl secrets set DATABASE_URL=postgres://...
List current secrets:
flyctl secrets list
Multi-Region Deployment
Add more regions to reduce latency for global users:
flyctl scale count 2 --region iad,ams
This runs one Machine in Virginia (iad) and one in Amsterdam (ams). Fly.io's Anycast network routes users to the closest one.
For database-backed Astro apps, consider using Fly.io's managed Postgres or LiteFS for SQLite replication across regions.
Custom Domains
Add your domain:
flyctl certs add yourdomain.com
flyctl certs add www.yourdomain.com
Point your DNS records to Fly.io:
Arecord pointing to the IPv4 address shown byflyctl ips listAAAArecord pointing to the IPv6 address
Fly.io handles SSL certificates automatically through Let's Encrypt.
Production Tips
Use health checks. Add a health check endpoint in your Astro app and configure it in
fly.tomlso Fly.io knows when your app is ready to receive traffic.Set memory limits. Scale memory with
flyctl scale memory 512if your on-demand routes need more headroom under load.Enable metrics. Fly.io provides built-in metrics through Grafana. Access them from the Fly.io dashboard to monitor response times and error rates.
Use volumes for persistent data. If your Astro app writes files (like uploaded images), attach a Fly Volume since Machine filesystems are ephemeral.
Set up CI/CD. Add
flyctl deployto your GitHub Actions workflow. This is the canonical workflow from Fly's Continuous Deployment with GitHub Actions guide.
# .github/workflows/fly-deploy.yml
name: Fly Deploy
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy app
runs-on: ubuntu-latest
concurrency: deploy-group
steps:
- uses: actions/checkout@v4
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
Generate the deploy token with fly tokens create deploy, then add it as a repository secret named FLY_API_TOKEN under Settings, Secrets and variables, Actions. Copy the entire token value including the FlyV1 prefix.
Common Errors and Fixes
- App deploys but Fly reports it is unreachable or health checks fail. The standalone Node server defaults to binding
127.0.0.1inside the container, so Fly's proxy cannot connect. SetENV HOST=0.0.0.0in your Dockerfile (and makeinternal_portinfly.tomlmatch thePORTthe server listens on). This is the single most common Astro-on-Fly mistake. See @astrojs/node docs for theHOST/PORTenv vars. - Astro errors out saying
output: 'hybrid'is not a valid option. That value was removed in Astro 5. Delete it. Static is the default, and adding an adapter is what enables on-demand rendering now. See the v5 upgrade guide. - Build fails with an unsupported Node version. Astro 6 requires Node
>=22.12.0(theenginesfield on theastronpm package). Bump your Dockerfile base image tonode:22-slimand your CI Node version to match. - Routes that should be dynamic are returning stale prerendered HTML. With static as the default, a route is only rendered on demand if you set
export const prerender = falseon it (or setoutput: 'server'project-wide). Mixing the two without realizing which is the default is a frequent gotcha. See On-demand rendering. flyctl launchdid not generate a Dockerfile. Fly only auto-generates a Node Dockerfile when it detects the@astrojs/nodeadapter in your config. Add the adapter first (npx astro add node), or write your own Dockerfile as shown above. See Deploy your Astro Site to Fly.io.- GitHub Action fails to authenticate. Make sure the repository secret is named exactly
FLY_API_TOKENand contains the full token fromfly tokens create deploy, including the leadingFlyV1prefix. See Continuous Deployment with GitHub Actions.
Official Docs and Examples
- Astro deploy guide for Fly.io: https://docs.astro.build/en/guides/deploy/flyio/
- Fly.io framework docs for Astro (quickstart and auto-detection): https://fly.io/docs/js/frameworks/astro/
@astrojs/nodeadapter reference: https://docs.astro.build/en/guides/integrations-guide/node/- Astro on-demand rendering guide: https://docs.astro.build/en/guides/on-demand-rendering/
- Fly continuous deployment with GitHub Actions: https://fly.io/docs/launch/continuous-deployment-with-github-actions/
- Example action wrapper repo (
superfly/flyctl-actions): https://github.com/superfly/flyctl-actions
Alternatives to Consider
- Vercel if you want zero-config deployment with edge functions and built-in Astro support.
- Cloudflare Pages if you prefer edge-first deployment with the Cloudflare ecosystem.
- Railway if you want a simpler PaaS experience without writing Dockerfiles.
Wrapping Up
Fly.io gives on-demand Astro apps the performance benefits of edge deployment without the constraints of serverless platforms. You get real VMs, multi-region routing, and fine-grained control over your infrastructure. The auto-stop feature keeps costs near zero for low-traffic sites, and scaling up is a single command. If your Astro site needs server-side rendering with global reach, Fly.io is one of the strongest options available. Just remember the Astro 6 essentials: static is the default, the Node adapter unlocks on-demand routes, and HOST=0.0.0.0 is mandatory inside Docker.
Sources
- Deploy your Astro Site to Fly.io (Astro Docs) (checked 2026-05-29)
- Run an Astro App (Fly.io Docs) (checked 2026-05-29)
- @astrojs/node adapter (Astro Docs) (checked 2026-05-29)
- On-demand rendering (Astro Docs) (checked 2026-05-29)
- Upgrade to Astro v5 (Astro Docs) (checked 2026-05-29)
- Install flyctl (Fly.io Docs) (checked 2026-05-29)
- Continuous Deployment with GitHub Actions (Fly.io Docs) (checked 2026-05-29)
superfly/flyctl-actions(GitHub) (checked 2026-05-29)astro6.4.2 on npm (engines: node >=22.12.0) (checked 2026-05-29)@astrojs/node10.1.2 on npm (peer: astro ^6.3.0) (checked 2026-05-29)
Related Articles
How to Use Algolia with Astro: Complete Guide
Step-by-step guide to integrating Algolia with your Astro website.
How to Integrate Auth0 with Astro: Complete Guide
Step-by-step guide to integrating Auth0 with your Astro website. Setup, configuration, and best practices.
How to Use AWS Amplify with Astro: Complete Guide
Step-by-step guide to integrating AWS Amplify with your Astro website.