komal.nakrani
Browse Themes
ThemesCategoriesDocsBlogTips & Tricks
Deployment••By Komal Nakrani

Deploying Astro: Every Major Host Compared

How to deploy Astro: run npm run build, publish dist/ to Cloudflare Pages, Netlify, Vercel or GitHub Pages. Settings, adapters, env vars and domains compared.

#Astro#Deployment#Cloudflare Pages#Netlify#Vercel#GitHub Pages

To deploy Astro, run npm run build and publish the dist/ folder to any static host. Cloudflare Pages, Netlify and Vercel detect Astro automatically when you connect a Git repository; you confirm the build command (npm run build) and output directory (dist), and every push deploys. You only need an adapter if some pages render on request.

This guide compares the major hosts, gives the exact settings for each, and covers environment variables, custom domains and the mistakes that cause most failed deploys.

Key Takeaways

  • A static Astro site is a folder of files. Any host that serves files can serve it.
  • Build command npm run build, output directory dist, Node.js 22.12 or newer. Astro 7 won’t build on older Node.
  • Install an adapter (npx astro add cloudflare, netlify, vercel or node) only for on-demand pages, server islands, actions or sessions.
  • GitHub Pages project sites need site and base set in astro.config.mjs.
  • Use astro:env to declare environment variables with types and validation.

What you’re deploying

Astro has two output shapes:

  • Static (default). astro build writes HTML, CSS, JS and assets to dist/. Upload that folder anywhere.
  • Static plus on-demand. With an adapter, Astro also builds a server entry for pages that render per request. The host runs it as a function or worker, and serves the static pages as files.

Start static. Most marketing sites, blogs, docs and portfolios never need anything else, and a static deploy has the fewest moving parts. The Astro SSR guide covers when the second shape is worth it.

The build, step by step

Locally, before connecting any host:

npm ci
npm run build
npx astro preview   # serves dist/ at http://localhost:4321

If npm run build succeeds locally and astro preview looks right, the host will almost certainly succeed too. Most failed deploys come from a difference between the two environments: Node version, a missing environment variable, or a file whose name differs only in case (macOS doesn’t care, Linux build servers do).

Pin the Node version so every environment agrees. Add an .nvmrc file:

22

and an engines field in package.json:

{
  "engines": { "node": ">=22.12.0" }
}

Host comparison

Cloudflare Pages Netlify Vercel GitHub Pages
Detects Astro Yes (preset) Yes Yes Via Actions workflow
Static hosting Yes Yes Yes Yes
On-demand adapter @astrojs/cloudflare @astrojs/netlify @astrojs/vercel No
Preview deploys per branch/PR Yes Yes Yes No
_redirects / _headers files Yes Yes No (use vercel.json) No
Custom domain + HTTPS Yes Yes Yes Yes

All four have free tiers that comfortably cover a typical static site; check each provider’s current pricing page for limits on builds, bandwidth and function invocations.

How to choose:

  • Cloudflare Pages if you want a large global network and may later use Workers, KV or R2 alongside your site.
  • Netlify if you want built-in forms, split testing and a mature UI with _redirects support.
  • Vercel if your team already uses it for other projects.
  • GitHub Pages for open-source docs or personal sites that live with their code and need nothing server-side.

Deploy to Cloudflare Pages

  1. Push your project to GitHub or GitLab.
  2. In the Cloudflare dashboard, open Workers & Pages, create an application, choose Pages, and connect the repository.
  3. Select the Astro framework preset. It fills in:
    • Build command: npm run build
    • Build output directory: dist
  4. Under environment variables, add NODE_VERSION = 22 if the build image defaults to an older Node.
  5. Save and deploy.

To deploy from your machine or CI without Git integration, use Wrangler:

npm run build
npx wrangler pages deploy dist --project-name my-site

Put _redirects and _headers files in public/ and Cloudflare applies them. The Cloudflare deploy docs for Komal themes walk through this with screenshots, and Cloudflare’s own Astro guide covers on-demand rendering with the adapter.

Deploy to Netlify

  1. In Netlify, choose Add new site and import from Git.
  2. Netlify detects Astro and sets:
    • Build command: npm run build
    • Publish directory: dist
  3. Deploy.

Or commit a netlify.toml so settings live with the code:

[build]
  command = "npm run build"
  publish = "dist"

[build.environment]
  NODE_VERSION = "22"

For on-demand pages, npx astro add netlify installs the adapter and Netlify runs those routes as functions.

Deploy to Vercel

  1. Import the repository in Vercel.
  2. Framework preset: Astro. Build command and output directory are filled in.
  3. Set the Node.js version to 22.x in project settings if needed.
  4. Deploy.

Or from the CLI:

npx vercel        # preview
npx vercel --prod # production

For on-demand routes, add the adapter with npx astro add vercel.

Deploy to GitHub Pages

GitHub Pages serves static files from a branch or an Actions artifact. Astro publishes an official action.

First, tell Astro where the site lives:

// astro.config.mjs
export default defineConfig({
  site: 'https://username.github.io',
  base: '/my-repo',   // omit for a username.github.io repo or a custom domain
});

Then add the workflow:

# .github/workflows/deploy.yml
name: Deploy to GitHub Pages
on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: withastro/action@v3
  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - id: deployment
        uses: actions/deploy-pages@v4

In the repository settings, set Pages > Source to GitHub Actions. Check the action versions against the Astro GitHub Pages guide when you set this up, since they change over time.

With a base, every hard-coded link must include it. Build links with import.meta.env.BASE_URL instead of starting them with /.

Deploy-ready themes

Every Komal theme is a static Astro 7 project that builds to dist/ with no adapter, so it deploys to any of these hosts unchanged. Browse Astro themes, or read the static hosting docs for Netlify, Vercel and GitHub Pages specifics.

Other hosts

Because the output is plain files, anything that serves static files works: AWS S3 with CloudFront, Azure Static Web Apps, Render, Firebase Hosting, a VPS running nginx or Caddy. Upload dist/ and configure the server to serve 404.html for missing paths.

For on-demand rendering on your own server, use the Node adapter:

npx astro add node
npm run build
node ./dist/server/entry.mjs

Environment variables with astro:env

Declare variables in config so Astro validates them at build or start time:

// astro.config.mjs
import { defineConfig, envField } from 'astro/config';

export default defineConfig({
  env: {
    schema: {
      PUBLIC_SITE_NAME: envField.string({ context: 'client', access: 'public', default: 'My Site' }),
      NEWSLETTER_API_KEY: envField.string({ context: 'server', access: 'secret' }),
    },
  },
});

Then import them from typed modules:

import { PUBLIC_SITE_NAME } from 'astro:env/client';
import { NEWSLETTER_API_KEY } from 'astro:env/server';

The rules:

  • client + public values are inlined into client code. Never put secrets there.
  • server + secret values are read at runtime on the server and never bundled.
  • Locally, values come from .env; on the host, set them in the dashboard.

A missing required variable fails with a clear message instead of rendering undefined. The environment variables guide covers import.meta.env for simpler cases.

Custom domains and HTTPS

Every host above issues HTTPS certificates automatically. The steps are the same everywhere: add the domain in the host’s dashboard, create the DNS records it shows you, wait for verification. After that, update site in astro.config.mjs to the new domain and redeploy so canonicals and the sitemap use it.

The custom domain tip covers apex vs www and the redirect between them, and the custom domains docs show the DNS records for each host.

Build speed

Small sites build in seconds. Builds slow down with many images (each one is processed) and with large content sources. Hosts cache node_modules between builds; caching Astro’s image cache as well helps most. See faster Astro builds in CI for the exact directories to cache.

Preview deploys and staging

Cloudflare Pages, Netlify and Vercel build every branch or pull request to its own URL. Use them:

  • Review content before it’s live. Writers share a preview link instead of screenshots.
  • Catch build failures early. A failing preview blocks the merge, not the production deploy.
  • Keep previews out of search. Preview URLs are public. Add a noindex header for non-production hosts, or generate robots.txt from an environment variable so only production allows crawling. The robots.txt tip shows how.

Set environment variables per environment in the host dashboard. A preview should never use production API keys that write data.

Rollbacks

Every host here keeps previous deploys. If a release breaks something:

  • Cloudflare Pages: Deployments → choose a previous production deploy → Rollback.
  • Netlify: Deploys → choose a deploy → Publish deploy.
  • Vercel: Deployments → choose a deploy → Promote (or Instant Rollback).
  • GitHub Pages: re-run the workflow for an earlier commit, or revert and push.

Rollbacks are instant on the first three because the old build is still stored. That’s one more reason to deploy static output: there’s no database migration to undo.

Troubleshooting failed deploys

Symptom Likely cause Fix
Node.js vX is not supported Host uses an older Node Set Node 22 via .nvmrc, NODE_VERSION or host settings
Works locally, Cannot find module on host File name case differs from import Match the import’s case to the file exactly
Blank page, assets 404 on GitHub Pages base missing or links ignore it Set base, build links with import.meta.env.BASE_URL
Missing environment variable Variable not set on the host Add it in the host dashboard for that environment
Every route shows the home page Host configured as a single-page app Remove the SPA rewrite, make sure 404.html exists
On-demand route returns 404 No adapter, or page lacks prerender = false Install the adapter, check the page export
Old content after deploy CDN or browser cache Purge the CDN cache, check Cache-Control headers

When the log is long, scroll to the first error. Later errors are usually consequences of it.

Deploying from CI instead of the host

Sometimes you want builds in your own CI (GitHub Actions, GitLab CI) and the host only to serve files: to run tests first, to build once and deploy to several places, or to keep build secrets out of the host. Build in CI, then upload dist/ with the host’s CLI:

npm ci
npx astro check
npm run build
npx wrangler pages deploy dist --project-name my-site   # Cloudflare Pages
# npx netlify deploy --dir=dist --prod                  # Netlify
# npx vercel deploy --prebuilt --prod                   # Vercel (after `vercel build`)

Store the host’s API token as a CI secret. You lose the host’s automatic preview comments on pull requests unless you add them to your workflow, so most teams start with the host’s Git integration and move to CI deploys only when they need the control.

Deploy checklist

  • npm run build and npx astro preview work locally
  • Node 22.12+ pinned with .nvmrc or a host setting
  • site set to the production URL (and base for GitHub Pages project sites)
  • Environment variables declared with astro:env and set on the host
  • Adapter installed only if you have on-demand routes
  • 404.astro exists
  • Redirects for any moved URLs
  • Custom domain verified, HTTPS active, one canonical host (www or apex)
  • Sitemap submitted in Search Console

FAQ

Where does Astro put the built site?

In dist/ at the project root. Change it with the outDir option in astro.config.mjs.

Do I need an adapter to deploy Astro?

Not for static sites. You need one only for pages that render on request, server islands, actions, sessions or middleware that runs per request.

Why does my Astro build fail on the host but work locally?

The usual causes are an older Node version on the host (Astro 7 needs 22.12+), a missing environment variable, or a file path that differs in letter case. Check the first error in the build log.

Can I deploy Astro to GitHub Pages?

Yes. Set site and base in config, add the withastro/action workflow, and set the Pages source to GitHub Actions.

What is the best host for Astro?

For a static site, Cloudflare Pages, Netlify and Vercel are all strong and free to start. Pick based on the extras you need (forms, edge storage, existing team accounts) rather than raw speed.