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:
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. The default Machine size is 256MB. For Astro SSR with moderate traffic, 512MB is safer. Scale with
flyctl scale memory 512.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. UseFLY_API_TOKENas 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.
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.