/ astro-integrations / How to Integrate Fly.io with Astro: Complete Guide
astro-integrations 5 min read

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 use SSR (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 hybrid rendering model pairs well with this because you can serve static pages from edge caches while running SSR routes on Fly Machines.

Prerequisites

  • Node.js 18+
  • An Astro project with SSR enabled (npm create astro@latest)
  • A Fly.io account (free tier includes 3 shared VMs and 160GB outbound transfer)
  • The Fly CLI (flyctl) installed

Install the Fly CLI:

# macOS
brew install flyctl

# Linux
curl -L https://fly.io/install.sh | sh

# Windows
powershell -Command "iwr https://fly.io/install.ps1 -useb | iex"

Log in:

flyctl auth login

Setting Up Astro for Fly.io

First, install the Node.js adapter since Fly.io runs your Astro app as a Node server:

npm install @astrojs/node

Update your astro.config.mjs:

import { defineConfig } from "astro/config";
import node from "@astrojs/node";

export default defineConfig({
  output: "hybrid",
  adapter: node({
    mode: "standalone",
  }),
});

The standalone mode produces a self-contained server that Fly.io can run directly.

Creating the Dockerfile

Fly.io uses Docker to package your app. Create a Dockerfile in your project root:

FROM node:20-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-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 ./

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 = true
  auto_start_machines = true
  min_machines_running = 0

[env]
  NODE_ENV = "production"

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 built-in 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:

  • A record pointing to the IPv4 address shown by flyctl ips list
  • AAAA record pointing to the IPv6 address

Fly.io handles SSL certificates automatically through Let's Encrypt.

Production Tips

  1. Use health checks. Add a health check endpoint in your Astro app and configure it in fly.toml so Fly.io knows when your app is ready to receive traffic.

  2. Set memory limits. The default Machine size is 256MB. For Astro SSR with moderate traffic, 512MB is safer. Scale with flyctl scale memory 512.

  3. Enable metrics. Fly.io provides built-in metrics through Grafana. Access them from the Fly.io dashboard to monitor response times and error rates.

  4. Use volumes for persistent data. If your Astro app writes files (like uploaded images), attach a Fly Volume since Machine filesystems are ephemeral.

  5. Set up CI/CD. Add flyctl deploy to your GitHub Actions workflow. Use FLY_API_TOKEN as a repository secret for authentication.

# .github/workflows/deploy.yml
name: Deploy to Fly.io
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    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 }}

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 Astro SSR 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.