Deploy Astro
Astro builds to static files by default, so a stock Astro project needs no changes at all. Connect the repository in the Quick Start and it deploys.
This page covers the settings that do need your attention: the ones Astro gets from its config rather than from us, and, if some pages must render per request, the deploybase adapter that runs them.
Build defaults
Section titled “Build defaults”| Setting | Value |
|---|---|
| Build command | npm run build |
| Output directory | dist |
| Image | node:24-alpine |
Detection is automatic when your repository has an astro.config.* file. Every value above is editable per project, and the full defaults table covers the other frameworks.
Set site before you go live
Section titled “Set site before you go live”Astro needs the production URL to generate absolute links, and it has no way to learn it from us:
import { defineConfig } from 'astro/config';
export default defineConfig({ site: 'https://example.com',});Leave it out and Astro.site is undefined, which silently breaks canonical tags, RSS feeds and @astrojs/sitemap. The pages still build, so nothing fails loudly. Set it to your custom domain if you have one, otherwise to your .sites.deploybase.eu URL.
Replace any other SSR adapter
Section titled “Replace any other SSR adapter”An adapter written for another host, such as @astrojs/node, @astrojs/vercel or @astrojs/netlify, produces output deploybase cannot run. A leftover one either fails the build or produces a dist/ full of server entrypoints that publishes as an empty site.
- If every page can be built ahead of time, delete the
adapterline andoutput: 'server', and uninstall the package. - If some pages must render per request, swap it for
@deploybase/adapter-astro, as the next section shows.
Render pages on demand (beta)
Section titled “Render pages on demand (beta)”@deploybase/adapter-astro runs the parts of your site that render per request, on a deploybase function in Paris (Node 22): on-demand pages, API routes, actions and server islands. Prerendered pages and assets are still served from the CDN.
Two conditions apply before you start:
-
Your team needs HTTP functions, which come with the Starter, Pro and Scale plans. On Free, the build still succeeds and the site goes live as a static site: the prerendered pages work, the on-demand routes answer 404, and the build log says why:
⚠️ This build includes server-rendered routes, which need a paid plan. Only the static pages were deployed; server routes will answer 404After upgrading, deploy again: the server is set up only when a deployment is built.
-
On-demand routes render on every address of the site: the project’s deploybase address, branch previews and custom domains, so
https://example-site.sites.deploybase.eu,https://staging-example-site.previews.deploybase.euandhttps://example.comall work.
Install and configure the adapter
Section titled “Install and configure the adapter”npm install @deploybase/adapter-astroimport { defineConfig } from 'astro/config';import deploybase from '@deploybase/adapter-astro';
export default defineConfig({ output: 'server', // every page renders on demand unless it opts into prerendering adapter: deploybase(),});Keep the project’s build defaults: build command npm run build, output directory dist. Nothing else needs setting on deploybase.
Choose which pages render on demand
Section titled “Choose which pages render on demand”Pick the default that matches most of your site, and mark the exceptions:
- Mostly dynamic:
output: 'server', andexport const prerender = trueon the pages that can be built ahead of time. - Mostly static: leave
outputat its default'static', and putexport const prerender = falseon the pages that must render on demand.
Prerender src/pages/404.astro with export const prerender = true. An on-demand 404 page means every missing path wakes the server just to say “not found”. The prerendered 404 and 500 pages are also bundled with the server, so it can answer with them straight away.
If nothing renders on demand (no on-demand pages, no actions, no server islands), the adapter writes a plain static site and no server is deployed at all.
Read environment variables at runtime
Section titled “Read environment variables at runtime”Variables with Runtime ticked on the project’s Environment page reach your server through getSecret() from astro:env/server, and through process.env:
import { getSecret } from 'astro:env/server';
export const prerender = false;
export async function GET() { const key = getSecret('QUOTES_API_KEY'); // ...}A production deployment gets the production values and a branch preview the preview values, each captured when the deployment is built. To change what a running server sees, redeploy after saving the variable. Rolling back to an older deployment brings back the values that deployment was built with.
What gets deployed
Section titled “What gets deployed”astro build writes everything deploybase needs into dist/.deploybase/v1/:
| Path | What it is |
|---|---|
config.json |
Which paths the server answers, and the 404 page |
static/ |
Client assets, prerendered pages and public/. Published to the CDN |
server/ |
The server, bundled into self-contained files with entry.mjs as its entry. Never public |
If you change Astro’s outDir, change the project’s output directory to match.
How requests reach the server
Section titled “How requests reach the server”- A request for a file that exists in
static/is served from the CDN. A static file always wins. - A
GETorHEADthat matches no file goes to your server only when its path matches one of your on-demand routes. Anything else gets the 404 page straight from the CDN, without starting the server. - Every other method (
POST,PUT,PATCH,DELETE,OPTIONS) goes to the server, on any path.
The adapter writes one route pattern per on-demand route, plus Astro’s own /_actions/, /_server-islands/ and /_image. A src/pages/[...slug].astro that renders on demand sends every missing path to the server.
Keep a package out of the server bundle
Section titled “Keep a package out of the server bundle”deploybase({ external: ['example-optional-telemetry'],});Packages listed in external are left out of the server bundle and are not installed on deploybase, so list only one whose import is optional or lazy. sharp is always left out.
What does not work with on-demand rendering yet
Section titled “What does not work with on-demand rendering yet”- Promoting a preview. A preview deployment’s server was built with the preview variables, so promoting it to production is refused with
409. Deploy the commit on the production branch instead. Rolling back to an older production deployment works. - Cold starts. The server scales to zero when it goes quiet, sooner on a branch preview than in production, and the first request after that waits several seconds. Prerender every page that can be built ahead of time. The functions reference has the idle windows.
- 50 second deadline. A response must start within 50 seconds, and a streamed response must also finish within that time.
- Request body size. A body over the HTTPS request limit is refused with
413. - No WebSockets. Server-sent events and streamed responses work.
HEADis rendered asGET. The page or endpoint runs in full and the body is dropped.- No edge caching of server responses. Every on-demand response is sent with
Cache-Control: private, no-store, whatever your code sets. Prerender what can be cached. - No native modules. The server is bundled into plain JavaScript and uploaded without
node_modules, so a package with a native binary cannot run. The build fails if the bundle still imports anything fromnode_modules, and names the package. - No on-demand image transformation. Images are optimised with
sharpat build time as usual, but/_imagedoes not work at request time. Use<Image>and<Picture>on prerendered pages, or images that need no transforming. - Sessions need a remote driver. Instances are short-lived and share no disk, so the build fails if
sessionuses the filesystem or memory driver, or has none. Use a Redis, Upstash or database driver fromsessionDriversinastro/config. i18n.domainsis not supported.- Up to 200 on-demand route patterns. Past that, every missing path is sent to the server instead, and the build log says so.
Only PUBLIC_ variables reach the browser
Section titled “Only PUBLIC_ variables reach the browser”Environment variables with Build ticked are injected during the build, so they are available to astro.config.mjs, to content loaders, and to anything running at build time. Astro then exposes only the PUBLIC_-prefixed ones to client code, which is Astro’s rule rather than ours.
const key = import.meta.env.PUBLIC_ANALYTICS_ID; // in the browser bundleconst secret = import.meta.env.API_SECRET; // build time onlyA PUBLIC_ variable is baked into the shipped JavaScript, so treat it as published. Anything you would not paste into a public repository does not belong in one.
When it breaks
Section titled “When it breaks”The site deploys but every page is blank, or assets 404. Your outDir is not dist. Astro writes wherever the config says, and we upload the directory the project is configured with. Match them: either remove outDir from astro.config.mjs, or set the project’s output directory to the same path.
Links and images work locally but 404 in production. A base set in astro.config.mjs prefixes every generated URL. You need it only when the site is served from a subpath, which is not the case on a deploybase domain. Remove it.
Content collections fail on a schema error. Collections are validated at build time, so a frontmatter field that does not match the Zod schema fails the whole build rather than one page. The build log names the file and the field.
The build cannot reach an API. Builds have outbound HTTPS, but private networks and cloud metadata endpoints are blocked, so a loader that fetches from an internal host has no route to it. Publish the data or fetch it at runtime from the browser instead.
Live a8b7eb1d built