# Supported frameworks

Source : https://webgune.cloud/en-us/docs/frameworks-supportes · Mis à jour le 2026-10-27

# Supported frameworks

On every deploy from GitHub, the builder clones your branch and analyzes the repository to identify the framework: it infers the build command, the artifacts to embed in the production image, and the **appropriate start command**. The selected framework and the generated command are shown in the build logs, at the "Framework detection" step.

This page is the complete reference: detection signals, generated commands, per-framework notes, and override methods.

## How detection works

Detection follows a strict order, from most specific to most generic:

1. **Dockerfile mode**, when chosen in the service settings: your Dockerfile takes precedence and no detection is applied (see [Overriding detection](#overriding-detection)).
2. **Meta-frameworks** (Next.js, Nuxt, SvelteKit, Remix, Astro…), detected before their underlying building blocks: a Blitz project isn't confused with the Next.js it embeds, a Qwik or Vike project isn't reduced to its Vite config.
3. **Python frameworks** (Django, FastAPI, Flask) and **static pipelines** (Gulp).
4. **Node.js backend frameworks** (NestJS, Fastify, Koa…). Express is tested last, since it appears as a direct dependency of many other frameworks.
5. **Generic per-language fallbacks**: Node.js with a `start` script, Laravel, Go, Ruby on Rails, Rust.

Two types of signals are used, in this order of confidence:

- **`package.json` dependencies** (`dependencies` + `devDependencies`). The most reliable signal: many frameworks work without a config file.
- **Conventional files**: `next.config.*`, `astro.config.*`, `go.mod`, `Cargo.toml`, `manage.py`, `artisan`…

## Resolving the start command (Node.js)

For "raw" Node frameworks (Express, Koa, Fastify…), the start command is resolved in order of preference:

1. **The `start` script in your `package.json`** → `npm start`. You know better than we do how to start your application.
2. **The `main` field**, if it points to an existing file → `node <main>`.
3. **The framework's conventional entry points** present in the repository (e.g. `app.js` for Koa, `server.js` for Fastify).
4. **The expected build output** as a last resort (e.g. `dist/main.js` for NestJS: the file doesn't exist at clone time but will be produced by the build).

**Compiled** frameworks (NestJS, AdonisJS, FoalTS, Marble.js) start directly from their build output rather than their development CLI: faster boot, no dev tooling required in the image.

## Fullstack & SSR

| Framework | Detection | Build | Generated start command |
|-----------|-----------|-------|------------------|
| Next.js | `next` or `next.config.*` | `next build` | `npm start` (`next start`) |
| Next.js standalone | `output: 'standalone'` in the config | `next build` | `node .next/standalone/server.js` |
| Blitz.js | `blitz` | `blitz build` | `npx blitz start` |
| Nuxt | `nuxt` or `nuxt.config.*` | `nuxt build` | `node .output/server/index.mjs` |
| SvelteKit | `@sveltejs/kit` or `svelte.config.*` | `vite build` | `node build/index.js` |
| Remix (classic) | `remix.config.*` | `remix build` | `npx remix-serve build` |
| Remix (Vite) | `@remix-run/*` without `remix.config` | `vite build` | `npx remix-serve build/server/index.js` |
| Astro (SSR) | `astro` + an `adapter` in the config | `astro build` | `node ./dist/server/entry.mjs` |
| Astro (static) | `astro` without adapter | `astro build` | `npx serve dist` |
| RedwoodJS | `@redwoodjs/*` or `redwood.toml` | `npx rw build` | `npx rw serve --port $PORT` |
| SolidStart | `@solidjs/start` | `vinxi build` | `node .output/server/index.mjs` |
| Qwik City (SSR) | `@builder.io/qwik-city` + `src/entry.express.tsx` | `qwik build` | `node server/entry.express.js` |
| Qwik City (static) | `@builder.io/qwik-city` without Express adapter | `qwik build` | `npx serve dist` |
| Gatsby | `gatsby` or `gatsby-config.*` | `gatsby build` | `npx serve public` |
| Vike | `vike` | `vite build` | custom server (`npm start` takes priority) |
| Fresh (Deno) | `deno.json` | None | `deno run -A main.ts` |

### Per-framework notes

- **Next.js**: standalone mode is detected automatically (`output: 'standalone'` in `next.config.*`): the final image only embeds `.next/standalone`, `.next/static`, and `public`. See also the [Next.js](/docs/web-nextjs) guide.
- **Nuxt / SolidStart**: both produce a Nitro output, and only the `.output` folder is kept in the final image.
- **SvelteKit**: the generated command assumes **adapter-node** (`@sveltejs/adapter-node`), which produces `build/index.js`. Serverless adapters (Vercel, Netlify…) don't produce a standalone server.
- **Remix**: both eras are handled: the classic compiler (`remix.config.*` present) and the Vite plugin (`@remix-run/*` dependency alone).
- **Astro**: SSR if an `adapter` is present in `astro.config.*` (use `@astrojs/node`), otherwise the site is served statically from `dist/`.
- **Qwik City**: run `npx qwik add express` for SSR. The presence of `src/entry.express.tsx` triggers server mode; otherwise the `dist/` output is served statically.
- **RedwoodJS**: build and serve go through the `rw` CLI, which handles the api + web monorepo.
- **Vike**: Vike assumes a custom server. Define a `start` script in your `package.json`; failing that, the builder looks for `server/index.js`, `server.js`, then `index.js`.
- **Fresh / Deno**: a different runtime. The auto builder uses the official Deno image: it runs the `start` task from your `deno.json` if there is one, otherwise the first entry file it finds (`main.ts`, `server.ts`…).

## Node.js backend

| Framework | `package.json` key | Generated start command |
|-----------|--------------------|------------------|
| Express | `express` | `npm start` › `main` › `index.js`, `server.js`, `app.js`, `src/*` |
| Fastify | `fastify` | `npm start` › `main` › `server.js`, `app.js`, `index.js`, `src/*` |
| NestJS | `@nestjs/core` | `node dist/main.js` |
| Koa | `koa` | `npm start` › `main` › `app.js`, `index.js`, `server.js`, `src/*` |
| Hapi | `@hapi/hapi` (or `hapi`) | `npm start` › `main` › `server.js`, `index.js`, `src/*` |
| Sails | `sails` | `npm start` › `main` › `app.js` |
| AdonisJS v6 | `@adonisjs/core` + `adonisrc.ts` | `node build/bin/server.js` |
| AdonisJS v5 | `@adonisjs/core` | `node build/server.js` |
| LoopBack 4 | `@loopback/core` | `npm start`, otherwise `node .` |
| FeathersJS | `@feathersjs/*` | `npm start` › `main` › `src/index.js`, `lib/index.js`, `dist/index.js` |
| Restify | `restify` | `npm start` › `main` › `server.js`, `index.js`, `src/*` |
| Egg.js | `egg` | `npx egg-scripts start --port $PORT` |
| Total.js | `total.js` (or `total4`/`total5`) | `npm start` › `main` › `index.js`, `debug.js` |
| FoalTS | `@foal/core` | `npm start`, otherwise `node build/index.js` |
| Marble.js | `@marblejs/core` | `npm start`, otherwise `node dist/main.js` |
| Moleculer | `moleculer` | `npm start`, otherwise `npx moleculer-runner services` |
| Seneca | `seneca` | `npm start` › `main` › `index.js`, `server.js`, `app.js` |
| Generic Node.js | `start` script present | `npm start` |

The notation `npm start › main › files…` refers to the [resolution order](#resolving-the-start-command-nodejs) described above.

### Per-framework notes

- **NestJS**: `nest start` recompiles on boot and requires the Nest CLI. The builder runs `dist/main.js` directly, produced by `npm run build` (`nest build`). Make sure your `build` script exists.
- **AdonisJS**: the version is inferred from the presence of `adonisrc.ts` (v6). The output paths differ between v5 and v6.
- **Egg.js**: `egg-scripts` runs in the foreground (no `--daemon`). Inside a container, the main process *is* the application.
- **Moleculer**: without a `start` script, the runner loads services from the `services/` folder.
- **LoopBack 4**: `node .` follows the `main` field (usually `dist/index.js`, produced by the TypeScript build).

## Static frontend

| Tool | Detection | Build | Generated start command |
|-------|-----------|-------|------------------|
| Vite (React, Vue, vanilla…) | `vite.config.*` | `vite build` | `npx serve dist -p $PORT` |
| Gulp | `gulpfile.*` | `npx gulp` | `npx serve <output> -p $PORT` |
| Gatsby | `gatsby` | `gatsby build` | `npx serve public -p $PORT` |

For Gulp, the output folder is extracted from the gulpfile's first `dest()` call; failing that, `dist/`, `www/`, then `build/` are tried. Only the output folder is embedded in the final image.

### The "Static site" nature

The table above describes automatic detection, which assumes a Node project. When you pick the **Static site** nature when creating the service (or later from its **Settings**), the build skips automatic detection entirely: the image produced is an **Nginx** serving your files.

- Repository of HTML/CSS/JS files: the folder containing `index.html` is published as-is (root, then `public`, `dist`, `build`, `site`, `_site`, `www`).
- Repository with a `build` script: it is run, and only its output is shipped (`dist`, `build`, `out`, `_site`, `.output/public`, `public`, `www`).
- Nginx listens on the service port, serves extension-less URLs (`/about` → `about.html`) and falls back to `index.html` for client-side routers.
- No repository at hand? The [Static site](/docs/templates-catalogue) template deploys the same Nginx with a persistent folder, editable from the **Files** tab.

## Other runtimes

| Framework | Detection | Generated start command |
|-----------|-----------|-------|
| Django | `manage.py` + `django` in requirements | `gunicorn <app>.wsgi:application --bind 0.0.0.0:$PORT` |
| FastAPI | `fastapi` in `requirements.txt` | `uvicorn main:app --host 0.0.0.0 --port $PORT` |
| Flask | `flask` in `requirements.txt` | `gunicorn app:app --bind 0.0.0.0:$PORT` |
| Laravel | `artisan` | `php artisan serve --host=0.0.0.0 --port=$PORT` |
| Ruby on Rails | `rails` in the `Gemfile` | `bundle exec rails server -b 0.0.0.0 -p $PORT` |
| Go | `go.mod` | `./<binary>` (module name) |
| Rust | `Cargo.toml` | `./target/release/<binary>` (crate name) |
| Deno | `deno.json` / `deno.jsonc` | `deno run -A main.ts` |

The dedicated [Python](/docs/web-python), [PHP](/docs/web-php), and [Go](/docs/web-go) guides detail the environment variables specific to each runtime.

## The port: the only requirement

Regardless of the framework, your application must **read the `PORT` environment variable** and **listen on `0.0.0.0`**, never on `localhost` or a hardcoded port. This is the #1 cause of deployments that build successfully but don't respond.

```js
// Node.js — applies to all frameworks
const port = process.env.PORT || 3000;
app.listen(port, "0.0.0.0");
```

## Overriding detection

Three mechanisms, from the lightest to the most complete:

### 1. Custom start command

From the dashboard, replace the generated command without touching the repository (see [Custom start command](/docs/commande-demarrage)).

### 2. `Procfile`

A `web: <command>` line in a `Procfile` at the root of your repository replaces the detected start command. It applies to every language except Node projects, which use the dashboard start command instead.

```text
web: gunicorn myapp.wsgi --bind 0.0.0.0:$PORT
```

### 3. Dockerfile mode

In the service settings, change the build type:

- **Dockerfile**: your repository fully drives the build, with no detection and your own rules (see [Docker](/docs/docker)).

## Package manager

The builder **normalizes every Node project on npm** at build time: it is the only package manager guaranteed to be present in every base image. In the cloned copy of your repository:

- the `bun.lock`, `bun.lockb`, `pnpm-lock.yaml`, `yarn.lock`, and `package-lock.json` lockfiles are removed: dependencies are cleanly reinstalled via `npm install`;
- the `packageManager` field (corepack) and the `engines.pnpm` / `engines.yarn` / `engines.bun` entries are stripped from `package.json`;
- config files belonging to another package manager (`pnpm-workspace.yaml`, `.yarnrc.yml`, `.yarn/`, `bunfig.toml`…) are removed;
- scripts calling another package manager are rewritten (`pnpm run build` → `npm run build`, `bunx prisma` → `npx prisma`, `yarn dlx` → `npx`…), and the `only-allow` guard, whose whole purpose is to make `npm install` fail, is dropped.

Your repository stays unchanged: only the copy used to build the image is modified. An out-of-sync lockfile can therefore never cause a deployment to fail.

Anything without an npm equivalent (`pnpm --filter`, `yarn workspace`, `bun run app.ts`) is left as-is and reported in the build logs, under the "Package manager" step. Those commands will fail: adjust the script in your repository, or switch to Dockerfile mode to drive the install yourself.

## Troubleshooting

- **Wrong framework detected**: check the build logs ("Framework detection" step). The typical case is a leftover dependency in `package.json` (e.g. `express` in a migrated project). Remove the dependency or switch to Dockerfile mode.
- **Build succeeds but the service doesn't respond**: almost always the [port](#the-port-the-only-requirement). Also check that your start command isn't listening on `localhost`.
- **`Cannot find module 'dist/main.js'`** on boot of a compiled framework: your `build` script isn't producing the expected output. Align the build output or define a `start` script.
- **Framework missing from this list**: The auto builder attempts generic per-language detection. If it fails, add a `Procfile` or switch to Dockerfile mode, and let us know which framework you're missing on [Discord](https://discord.webgune.cloud/).
