Appearance
How builds work
Services with a GitHub source are built by the Fjordrun builder. It clones the commit, decides how to build it, and produces a container image. Services from a Docker image or a template skip the build.
Decision order
The builder looks at the service's root directory and takes the first match:
- A Dockerfile —
Dockerfile,dockerfileorContainerfile, or the path set in Settings → Build → Dockerfile path. A repository Dockerfile always wins, whatever else is in the repository. See Dockerfiles. - Nixpacks selected — if you set the builder to Nixpacks in the service settings.
- A Node.js project (
package.json) — the builder detects the framework and generates a multi-stage Dockerfile for it (below). - A plain static site — an
index.htmlat the root, or only indist/,public/,build/,site/,www/,out/or_site/— served by nginx. - Anything else (Python, Go, Ruby, PHP, ...) — built with Nixpacks.
The build log starts with the decision, and the deployment stores it as its build plan:
text
Fjordrun builder: detected Vite + React (static) · npm · Node 22
install npm ci
build npm run build
serve dist/ with nginx (port $PORT)Node.js frameworks
For a Node.js project, the builder reads package.json (dependencies, scripts, engines), the lockfile and the framework's config file, and picks one of three ways to run it.
Static sites, served by nginx
These are built with your build script and the output is served by nginx (non-root nginx-unprivileged) on $PORT, with gzip, long caching for content-hashed assets, no-cache for HTML, and a /healthz endpoint. Single-page apps get a fallback to index.html for client-side routes; multi-page sites return real 404s (using your 404.html if there is one).
| Framework | Detected when | Output served |
|---|---|---|
| Vite (React, Vue, Svelte, Solid, Preact, Lit) | vite with a vite.config.*, an index.html or a vite build script | dist (or build.outDir) |
| Create React App | react-scripts | build |
| Vue CLI | @vue/cli-service | dist |
| Angular (no SSR) | @angular/core | outputPath from angular.json (dist/<project>/browser) |
| Astro (static) | astro without a server adapter | dist (or outDir) |
| Nuxt (static) | ssr: false, a static preset, or nuxt generate as the build script | .output/public |
| Next.js (export) | output: 'export' in next.config.* | out |
| SvelteKit (static) | @sveltejs/adapter-static | build (or pages) |
| React Router 7 (SPA) | ssr: false in react-router.config.* | build/client |
| Gatsby | gatsby | public |
| Docusaurus | @docusaurus/core | build |
| VitePress | vitepress | <src>/.vitepress/dist |
| Eleventy | @11ty/eleventy | _site (or --output) |
If the expected folder is missing after the build, the builder also tries dist, build, out, .output/public, public, _site, site and www.
Server-side rendering and servers, run with Node.js
These run their own server as the unprivileged node user:
| Framework | Runs |
|---|---|
| Nuxt (SSR) | node .output/server/index.mjs |
Next.js with output: 'standalone' | node server.js from the standalone output |
| Next.js (default) | next start -H 0.0.0.0 -p $PORT |
Astro with @astrojs/node (standalone) | node ./dist/server/entry.mjs |
Astro with @astrojs/node (middleware) | your start script |
SvelteKit with @sveltejs/adapter-node | node build |
Angular with @angular/ssr | node dist/<project>/server/server.mjs |
| Remix / React Router 7 (SSR) | your start script, else remix-serve / react-router-serve |
| NestJS | start:prod script, else node dist/main |
| Express, Fastify, Koa, Hono, hapi, others | your start script, else node + main, server.js, index.js, app.js... |
Development dependencies are pruned after the build when the run command does not need them.
Unsupported adapters
Some framework adapters produce output that only runs on one hosting provider. The build stops with a clear message instead of deploying something that cannot start:
- Astro with
@astrojs/vercel,@astrojs/netlify,@astrojs/cloudflareor@astrojs/deno→ use@astrojs/node(mode: 'standalone') or remove the adapter for a static site. - SvelteKit with
adapter-autoor a provider adapter → use@sveltejs/adapter-nodeor@sveltejs/adapter-static.
Monorepos
- Set the root directory to the app's folder, or
- leave it at the repository root: if the root
package.jsondelegates to a sub-folder (npm --prefix web run build,cd web && ...,pnpm --filter web build,npm -w web ...), or is a workspace with exactly one framework app, the builder finds the app. Workspaces are installed at the root.
Other languages: Nixpacks
Python, Go, Ruby, PHP, Java, Rust, Elixir and other languages fall back to Nixpacks. See the guides for Python and Go. You can also select Nixpacks explicitly in the service settings.
What gets ignored
The builder deletes macOS metadata files (._* and .DS_Store) from the checkout before building, and never sends .git or node_modules into the build context.
Next
- Build settings: root directory, build and start command overrides, watch paths
- Build variables:
VITE_*,NEXT_PUBLIC_*and other build-time values - Node.js and package managers
- Build resources: memory per plan and build timeouts
- Troubleshooting