Skip to content

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:

  1. A Dockerfile — Dockerfile, dockerfile or Containerfile, or the path set in Settings → Build → Dockerfile path. A repository Dockerfile always wins, whatever else is in the repository. See Dockerfiles.
  2. Nixpacks selected — if you set the builder to Nixpacks in the service settings.
  3. A Node.js project (package.json) — the builder detects the framework and generates a multi-stage Dockerfile for it (below).
  4. A plain static site — an index.html at the root, or only in dist/, public/, build/, site/, www/, out/ or _site/ — served by nginx.
  5. 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).

FrameworkDetected whenOutput served
Vite (React, Vue, Svelte, Solid, Preact, Lit)vite with a vite.config.*, an index.html or a vite build scriptdist (or build.outDir)
Create React Appreact-scriptsbuild
Vue CLI@vue/cli-servicedist
Angular (no SSR)@angular/coreoutputPath from angular.json (dist/<project>/browser)
Astro (static)astro without a server adapterdist (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-staticbuild (or pages)
React Router 7 (SPA)ssr: false in react-router.config.*build/client
Gatsbygatsbypublic
Docusaurus@docusaurus/corebuild
VitePressvitepress<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:

FrameworkRuns
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-nodenode build
Angular with @angular/ssrnode dist/<project>/server/server.mjs
Remix / React Router 7 (SSR)your start script, else remix-serve / react-router-serve
NestJSstart:prod script, else node dist/main
Express, Fastify, Koa, Hono, hapi, othersyour 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/cloudflare or @astrojs/deno → use @astrojs/node (mode: 'standalone') or remove the adapter for a static site.
  • SvelteKit with adapter-auto or a provider adapter → use @sveltejs/adapter-node or @sveltejs/adapter-static.

Monorepos ​

  • Set the root directory to the app's folder, or
  • leave it at the repository root: if the root package.json delegates 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 ​

Fjordrun is hosted in the EU (Hetzner, Nuremberg, Germany). Private beta.