Skip to Content

Deploy an Astro Site

Push your Astro project to GitHub and Faable builds and serves it — no Dockerfile, no YAML. The builder detects Astro from your package.json, installs your dependencies, runs npm run build, and serves the contents of dist/ directly, with no container, behind automatic SSL at https://<app>.faable.link, hosted in Europe.

Astro has two output modes, and they deploy differently. Which one you are on decides everything below, so check astro.config.mjs first.

Static Astro — the default

If your config has no output (or output: 'static'), astro build writes real HTML files per page into dist/. That is the zero-config path:

  1. The builder detects astro in your dependencies or devDependencies.
  2. It installs, runs your build script, and serves only dist/, directly and with no container — no Node.js process boots and there is no cold start.
  3. SPA fallback stays off. Astro builds a real file per route, so an unknown path 404s instead of rewriting to index.html. That is the correct behaviour for a multi-page site; it is not a bug.

Nothing else to do. Push and it is live.

Server-rendered Astro — add a start script

If your config sets output: 'server' or output: 'hybrid' with an adapter such as @astrojs/node, astro build writes something else entirely:

dist/ ├── client/ ← assets └── server/ └── entry.mjs ← your server

There is no index.html at the root of dist/. If you deploy this with no start script, the build goes green, Faable serves dist/ as a static directory, and every route — the homepage included — 404s. It looks like a successful deploy that serves nothing.

Add a start script so the builder runs your server instead of serving files:

{ "scripts": { "build": "astro build", "start": "node ./dist/server/entry.mjs" } }

A start script that runs your own server switches the app to a Node.js container — which is exactly what you want here.

Bind to 0.0.0.0

The @astrojs/node standalone server reads HOST and PORT from the environment. Faable injects PORT; it does not set HOST, and the adapter’s default is localhost, which nothing outside the container can reach. Set it once:

faable deploy secrets set HOST=0.0.0.0

Or configure it in the adapter and skip the variable. Either way, the rule is the $PORT contract: listen on 0.0.0.0 at process.env.PORT.

Deploy

From the dashboard — the normal path:

  1. Create a Project and an App in the Faable Dashboard .
  2. Click Link repository and pick your Astro repo.
  3. Push to your release branch. Faable builds and takes it live.

Or from your laptop, for an ad-hoc deploy:

npm i -g @faable/faable faable login faable deploy

Your site is ready at https://<app>.faable.link, with automatic SSL and the WAF already inspecting traffic.

Environment variables

Astro exposes variables prefixed PUBLIC_ to client-side code, compiled into the bundle at build time. So:

  • Set them before the build, with the CLI or the dashboard, then redeploy.
  • A PUBLIC_ variable is public — it ships inside the JavaScript your visitors download. Never put a secret there.
faable deploy secrets set PUBLIC_SITE_URL=https://example.com

On a static site there is no server, so non-PUBLIC_ variables only exist during the build. On a server-rendered site they are also readable at runtime through import.meta.env / process.env.

See Environment & Releases.

A different output directory

Faable expects the Astro default, dist/. If astro.config.mjs sets outDir elsewhere, the build fails the post-build check with Static build output 'dist' not found after the build. Drop the override, or force the static buildpack in faable.json:

{ "buildpack": "static" }

Monorepos

If the Astro site lives in a subdirectory, point rootDir at it in faable.json at the repository root:

{ "rootDir": "apps/site" }

See Monorepos.

Troubleshooting

  • The build is green but every route 404s — you are on output: 'server' with no start script, so a server build is being served as static files. See above.
  • A sub-page 404s on a static site — that page was not built. Check your routes and any getStaticPaths. Astro deliberately does not rewrite unknown paths to index.html.
  • Requests time out on a server-rendered site — the adapter is bound to localhost. Set HOST=0.0.0.0.
  • Static build output 'dist' not found — outDir is not dist/, or the build wrote nothing.
  • A PUBLIC_ variable is empty — it was set after the build. Redeploy.

Last updated on