# ember-cli-fastboot: server-side rendering for an existing Ember app

> ember-cli-fastboot renders an Ember application on the server and serves HTML to clients that never download the JavaScript assets. It plugs into the standard ember-cli build, sandboxes server-side code behind a package allowlist, and asks you to make browser-only code tolerate running in Node.

**ember-fastboot/ember-cli-fastboot** — Server-side rendering for Ember.js apps

- Repository: https://github.com/ember-fastboot/ember-cli-fastboot
- Website: http://ember-fastboot.com/
- Stars: 848 · Forks: 160
- Language: JavaScript
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/ember-fastboot-ember-cli-fastboot

## FastBoot renders the app in Node and ships HTML instead of a JS bundle

The premise of FastBoot is narrow and concrete: it renders and serves Ember.js apps on the server, so you can serve rendered HTML to browsers and other clients without requiring them to download JavaScript assets. There is no separate template language and no second rendering model. The same Ember application that runs in the browser runs in Node, and the output is HTML.

The target audience is therefore apps whose first paint matters: content sites, storefronts, anything a crawler has to read, or a user on a connection where shipping a full application bundle before the first meaningful screen is a bad trade.

The cost is stated in the same breath as the benefit. Some application code, add-ons or other dependencies may need to be modified to work when rendered server side, and the example given is that you cannot call the window object during FastBoot. This is the central constraint of the whole project, and it is a code change in your app, not a configuration flag.

The other notable claim in the documentation is about performance rather than reach: you may be shocked to learn that minified code runs faster in Node than non-minified code, so for anything serious you probably want the production environment build rather than the development one. That is a Node behaviour rather than a FastBoot trick, but it changes how you should run the app locally when you are judging the output.

The project is MIT licensed, and the repository is a pnpm workspace with a packages/ directory holding the individual packages.

## Installing is one ember command against an Ember 3.28 app

FastBoot requires Ember 3.28 or higher, and the documentation says it is preferable that the app is running ember-cli 3.28.0 and higher. That version floor is the real prerequisite, because the addon hooks into the ember-cli build pipeline.

The install is a single command run from inside the Ember CLI application.

```sh
ember install ember-cli-fastboot
```

After that, the app is served through the normal development command, and the FastBoot behaviour is on by default.

```sh
ember serve
```

The port defaults to 4200 and can be changed with a flag.

```sh
ember serve --port 8088
```

A detail worth knowing for local work is that FastBoot serving can be turned off at runtime with a query parameter, which lets you compare the server-rendered output against the client-rendered output on the same page.

```sh
ember serve --environment production
```

The same switch exists as an environment flag, so you can start the server with FastBoot disabled entirely, which is useful when a change breaks server rendering and you need to fall back to a client-rendered app to keep working.

The documentation also notes that if you have disabled building the FastBoot assets with the same flag, you should disable serving them as well when using ember serve, so that the built and served states do not diverge.

## The server sandbox only lets your app require packages you listed

FastBoot runs your application code in a sandbox, and the sandbox has a package allowlist. The reason given is security: an Ember app running in FastBoot can only access packages that have been explicitly whitelisted.

The allowlist lives in your app's package.json under a fastbootDependencies array.

```js
{
  "name": "my-sweet-app",
  "version": "0.4.2",
  "devDependencies": {
    // ...
  },
  "dependencies": {
    // ...
  },
  "fastbootDependencies": [
    "rsvp",
    "path"
  ]
}
```

The meaning is literal: the only node modules your Ember app can use are rsvp and path in that example. If the package is not built into Node itself, you must also add it with a version to the dependencies hash. Built-in modules such as path and fs only need the fastbootDependencies entry.

Requiring a package from application code goes through FastBoot.require, which is described as identical to the CommonJS require except that it checks every request against the whitelist first.

```js
let path = FastBoot.require('path');
let filePath = path.join('tmp', session.getID());
```

Attempting to require a package that is not whitelisted raises an exception. That is a hard failure at runtime, which is the right behaviour for a security boundary but does mean the allowlist has to be maintained as dependencies are added.

The FastBoot global itself is only available when running in FastBoot mode, so the documentation advises either guarding against its presence or using it only in FastBoot-only initialisers.

## The fastboot service tells the app where it is running

FastBoot registers a service named fastboot, and it exists so application code can ask where it is executing. The service exposes fastboot.isFastBoot, a boolean you check before touching anything server-specific, and fastboot.request, which exposes details about the current request being handled.

The request object is what gives a FastBoot app access to cookies for the current request, so server-rendered pages can be personalised per visitor rather than rendering a generic shell.

The most interesting method on the service is deferRendering. By default FastBoot waits for the beforeModel, model and afterModel hooks to resolve before sending the response to the client. If your asynchrony runs outside those contexts, the response may not reflect the state you want, because the page is sent before that work finishes.

deferRendering takes a promise and chains all the promises passed to it, and the server waits until every one of them resolves before sending the response. The catch is timing: the promises must be chained before rendering is complete after the model hooks.

The documented use case is a component that loads data in its init hook. Registering that promise lets the component defer the rendering of the whole page until the data is in place.

```js
import Component from '@glimmer/component';

export default class MyComponent extends Component {
  @service fastboot;
  @service model;

  constructor(owner, args) {
    super(owner, args);

    let promise = this.store.findAll('post').then((posts) => {
      this.posts = posts;
    });

    if (this.fastboot.isFastBoot) {
      this.fastboot.deferRendering(promise);
    }
  }
}
```

The call has to be wrapped in a fastboot.isFastBoot check, because the method throws an exception outside of that context. That guard is not stylistic; it is required.

## Config lives in config/fastboot.js, and the app server needs it passed explicitly

When running locally with ember serve, options are passed into the FastBoot instance through a config/fastboot.js file. The documentation is clear that this configuration file applies only to applications and is not supported for addons.

The file exports a function that receives the environment and returns a configuration object.

```js
module.exports = function(environment) {
  let myGlobal = environment === 'production' ? process.env.MY_GLOBAL : 'testing';

  return {
    buildSandboxGlobals(defaultGlobals) {
      return Object.assign({}, defaultGlobals, {
        myGlobal,
      });
    }
  };
}
```

The buildSandboxGlobals hook is the interesting one, because it is how you inject values into the sandbox your application code runs in. The environment argument is what lets the same file behave differently in production and in development.

One option is off limits. distPath is provided internally by ember-cli-fastboot and therefore cannot be modified by this file, which is sensible since the path to the built assets is part of how the addon is wired up. The FastBoot package README is the place to look for the full list of available options.

The production path is different, and this trips people up. When you use the FastBoot App Server, you have to pass the options from config/fastboot.js manually, because that server does not read the file the way ember serve does.

```js
const FastBootAppServer = require('fastboot-app-server');
const config = require('./config/fastboot')(process.env.NODE_ENV);

let server = new FastBootAppServer({
  distPath: 'dist',
  ...config,
});

server.start();
```

The config is invoked with process.env.NODE_ENV, so the same function that ember serve calls with an environment name is called here with an environment variable, and the resulting object is spread into the server options alongside the distPath.

## The repository is a pnpm workspace of several packages

The repository is a monorepo rather than a single addon. The root package.json is private, and the packages/ directory holds the individual packages, which the test scripts name explicitly: ember-cli-fastboot itself, fastboot, fastboot-app-server, fastboot-express-middleware, and a basic-app and custom-fastboot-app pair used for the mocha-level tests, plus an integration-tests package.

The root scripts are therefore a fan-out across the workspace rather than a single test command. The test script runs everything through npm-run-all, and each named script filters the workspace to one package, with separate entries for the ember-cli-fastboot addon, the fastboot package, the app server, the express middleware, the two app fixtures and the integration tests.

A few repository details tell you how the project operates. The package manager is pinned to pnpm with an integrity hash, there is a pnpm-workspace.yaml, a patches/ directory, a release-plan.json and a RELEASE.md, and the publishConfig points at the public npm registry. The release-plan setup means version bumps are proposed through pull requests rather than edited by hand.

The presence of a fastboot-express-middleware package is worth noting for deployment. It means the FastBoot app server can be mounted inside an existing Express application rather than run as a standalone server, which is what you want when the app has to share a process with other endpoints.

The main package is published to npm as ember-cli-fastboot, and the most recent tags are v6.0.0-ember-cli-fastboot on 2026-09-11, v5.0.3 on 2026-09-05 and v5.0.2 on 2026-08-28, so the project is shipping actively across a major version boundary.

## Where FastBoot is the wrong tool

The first wrong-tool case is an application that touches browser globals during setup. The documentation names window explicitly. If an addon reads window at module scope, or a route or component initializer assumes a DOM, server rendering fails, and the fix is in the addon or in your own code. In a project with a long tail of third-party addons, auditing that surface is the real cost of adopting FastBoot, and the documentation is candid that some addons will need modification.

The second case is a project that does not need HTML before JavaScript. If your app is behind a login and is a single-page tool, the crawlers that FastBoot helps do not see it and the first-paint argument does not apply. You would be adding a server, a sandbox and an allowlist to maintain for no benefit.

The third case is a team that cannot accept a global that exists only in one mode. FastBoot.require and the FastBoot global are unavailable in the browser, so any code path touching them needs a guard. The isFastBoot check is the documented pattern, and the deferRendering example shows it, but it is a permanent conditional in your codebase rather than a one-off.

On maintenance, the project is MIT licensed with the LICENSE.md file at the root, and the release history shows a v6.0.0 tag alongside v5.0.x releases within the same month, which tells you the major version is moving. The Ember 3.28 floor is worth rechecking against your app, since an addon that works on 6.0.0 may still carry the constraints of the version it was written for.

## Conclusion

ember-cli-fastboot suits a team with an existing Ember application that needs server-rendered HTML for crawlers, slow connections or a read-only first paint, and the install is a single ember install command against an Ember 3.28 or higher app. It does not suit a codebase whose addons reach for window during initialisation, because the server has no browser globals and the fix belongs in application code. Before adopting, read the app for direct window access, add every Node package the server will need to fastbootDependencies, and remember that the FastBoot global exists only in FastBoot mode, so guard it with fastboot.isFastBoot.

## FAQ

### How do I install FastBoot in an Ember application?

FastBoot requires Ember 3.28 or higher, and it is preferable that the app runs ember-cli 3.28.0 and higher. From within the Ember CLI application, run ember install ember-cli-fastboot, then serve the app with ember serve.

### How do I allow a Node package to be used inside FastBoot?

Add the package name to the fastbootDependencies array in your app's package.json. Packages that are not built into Node must also be listed with a version in the dependencies hash, while built-in modules such as path and fs only need the fastbootDependencies entry.

### How do I disable FastBoot while developing?

You can disable FastBoot serving at runtime with the fastboot query parameter, for example visiting the app at http://localhost:4200/?fastboot=false. You can also start the server with the environment flag FASTBOOT_DISABLED=true ember serve.

### What does the fastboot service provide in an Ember app?

The fastboot service lets you check whether you are running inside FastBoot with fastboot.isFastBoot, and it exposes the current request under fastboot.request, which is how you access cookies for the request being handled. Its deferRendering method lets you chain promises so the server waits before sending the response.

### How do I configure the FastBoot app server for production?

When using the FastBoot App Server you have to pass the options from config/fastboot.js manually, by requiring the config file with process.env.NODE_ENV and spreading the result into the FastBootAppServer options alongside distPath. The distPath option itself is provided internally by ember-cli-fastboot and cannot be changed from the config file.

## Sources

- [ember-fastboot/ember-cli-fastboot on GitHub](https://github.com/ember-fastboot/ember-cli-fastboot)
- [License: MIT](https://github.com/ember-fastboot/ember-cli-fastboot/blob/master/LICENSE)
- [Project website](http://ember-fastboot.com/)
- [README](https://github.com/ember-fastboot/ember-cli-fastboot/blob/master/README.md)
- [Releases](https://github.com/ember-fastboot/ember-cli-fastboot/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ember-fastboot-ember-cli-fastboot
