Node.js

Node.js is supported by Scalingo, furthermore, custom support has been added to manage the Meteor framework.

Standard Node.js Applications

Node.js App Detection

The file package.json should be present at the root of the project.

Dependencies Installation

The dependencies of your project are installed either with the npm package manager or the Yarn package manager.

If a yarn.lock file is present at the root of your project, yarn will be used to install the dependencies and run scripts, otherwise npm will build the dependencies during the deployment.

If a package-lock.json file is present at the root of your project and you are using npm version 6 or higher, Scalingo will use npm ci (for clean-install) to install your dependencies. Otherwise Scalingo will fallback to npm install.

Private Dependency

NPM Private Modules

First you need to create a read-only token with the NPM account you want to authenticate:

npm token create --read-only

Then set this token as environment variable (replace the 0-value by the token obtained in the previous command):

scalingo env-set NPM_TOKEN=00000000-0000-0000-0000-00000000

Add the following NPM configuration file .npmrc to the root directory of your project:

//registry.npmjs.org/:_authToken=${NPM_TOKEN}

Add this file and commit it in your Git repository, the environment variable will be automatically used during the build.

Configuring NPM

Sometimes a project needs custom npm behavior to set up proxies, use a different registry, etc. For such behavior, simply include an .npmrc file in the root of your project.

# .npmrc
registry = 'https://custom-registry.com/'

npm will also read the configuration from any environment variable starting with NPM_CONFIG.

Private Modules from SCM (GitHub/GitLab/etc.)

Your private module should be fetched from the SCM service through SSH. You can specify it this way:

"package-name": "git+ssh://git@<SCMhostname>/<workspace>/<repo>.git#master"

To authenticate to the SCM service, you need to use our SSH Private Key Buildpack, this buildpack will let you setup a private SSH key securely in the build environment.

devDependencies Installation

By default, dependencies present in the devDependencies field are installed. At the end of the deployment, devDependencies are pruned. Hence only production dependencies are left in the image used for the runtime.

The pruning step can be skipped by setting the environment variable YARN_PRODUCTION to any value.

Ensure you’re Tracking all your Dependencies

It is important to ensure you are tracking all the dependencies of your project in the package.json file. Otherwise your app may build but will potentially crash during its runtime, failing to find a dependency.

A common mistake is to run npm install without the --save flag. In this case the dependency is installed in the node_modules directory, but nothing is tracked in the package.json file.

You should also avoid to install global packages (i.e. using the -g flag). In this case packages are installed at the system level and are not tracked for your project.

In a nutshell: you should always use the following command when adding a dependency:

npm install --save <module>

Do not Track Modules with Git

When working locally on your code, after running npm install, a directory node_modules containing the dependencies of your application is created. You should not check this directory in your Git repository. If the dependency is listed in your package.json file, it will automatically be installed during the deployment of your application.

Adding these files to Git will create confusion and unnecessary noise in your code history and we consider it’s a good practice to keep it clean, only add the useful code (the code of your project). Moreover there is an issue with dependencies which build binaries during their installation. If you add these binaries to your repository, it may create incompatibility with Scalingo runtime environment.

The good method to avoid checking in these files is to add the following content in a .gitignore file at the root of your project and commit this file:

node_modules
bower_components
# Any other file created by grunt/gulp/webpack builds

Once done, Git will ignore these directories when making commits.

Specifying a Node.js Version

In your engines section of your package.json file, you can specify a version of Node.js to use.

If no version is defined, Scalingo installs the latest version of v20. Available versions of Node.js are available here: https://nodejs.org/en/about/releases/.

It is exactly the same with npm if you want to use a specific version of it.

{
  "name": "my-app",
  "engines": {
    "node": "20",
    "npm": "6"
  }
}

Node.js App Startup

What we use in order of priority

  1. Procfile at the root of your project
  2. The field .scripts.start defined in your package.json

Example of package.json:

{
  ...
  "scripts": {
    "start": "node server.js"
  }
}

Node.js Build Hooks

If your application has a build step that you would like to run when you deploy, you can use the build step defined in the package.json:

{
  ...
  "scripts": {
    "start": "node server.js",
    "build": "grunt build"
  }
}

If the package.json has a build script which needs to be customized for Scalingo, you should define ascalingo-postbuild step:

{
  ...
  "scripts": {
    "build": "grunt build",
    "scalingo-postbuild": "grunt build --prod",
    "start": "node server.js"
  }
}

You may also want to run a custom command before the installation of dependencies. This is possible thanks to commands named scalingo-prebuild:

{
  ...
  "scripts": {
    "scalingo-prebuild": "node prebuild.js",
    "start": "node server.js"
  }
}

Custom Cache Folder

By default, Scalingo stores the directories bower_components and node_modules at the root of the project in the deployment cache. But you might need to add different folders to this cache. You can override these defaults by specifying the cacheDirectories (or cache_directories) key at the root of your package.json file.

For example, if your application has a client and a server in the same repository, you can specify these folders to store them in the deployment cache with:

"cacheDirectories": ["client/node_modules", "server/node_modules"]

Meteor Application

If a .meteor file is detected at the root of your project, your app will be considered as a Meteor application.

See the Meteor applications documentation

Framework Requiring to Serve Static Files

Some front-end JavaScript frameworks (such as React Native, Ember.js, Ionic, Next.js, GatsbyJS) build static files and need a minimal web server to serve them. In order to do so, first update your package.json to at least specify the following scripts:

{
  // 
  "scripts": {
    "build": "<build script>",
    "start": "node server.js"
  },
  // 
}

The build script depends on the framework you use:

  • Ember.js: ember build --environment=production
  • GatsbyJS: gatsby build
  • Ionic: ionic-app-scripts build
  • Next.js: next build

The server.js file is a minimal web server such as the one presented here to serve the file generated during the build phase.

Buildpack

More information at https://github.com/Scalingo/nodejs-buildpack.

Next.js

Out of the box, Next.js is configured with a package.json which looks like:

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "next": "^10.0.4",
    "react": "^17.0.1",
    "react-dom": "^17.0.1"
  }
}

To make it work on Scalingo, you need to provide the port. Otherwise, you would get a timeout error.

....
 Waiting for your application to boot...
 !   Error deploying the application
 !   → Timeout: my-app took more than 60 seconds to boot
....

To fix this issue, modify the start script by adding -p $PORT

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start -p $PORT"
  },
  "dependencies": {
    "next": "^10.0.4",
    "react": "^17.0.1",
    "react-dom": "^17.0.1"
  }
}

Standalone Mode

The Next.js built image can be quite large, easily exceeding 500MB. If you encounter issues with the image size, you can try “standalone” mode.

More information here: Next.js in standalone mode.

Vite

In package.json, you need

  "scripts": {
    "start": "vite --port $PORT",
    // others scripts
    }
  }

In the vite.config (or vite.config.ts) file you need to add the following

  server: {
    host: "0.0.0.0",
  },

As a note, vite --port $PORT may compile components, depending on your configuration. If it does, it leads to a very long first HTTP request that may even crash your container. In this case, in the package.json, replace the start rule content to use node server.js (see section Framework Requiring to Serve Static Files) or vite preview --port $PORT.

AngularJS

Please refer to the AngularJS dedicated page for instructions on how to deploy such an application on Scalingo.

Yarn Known Issues

Catch SIGTERM In an Application Started With Yarn

You may want to catch the SIGTERM signal in your Node.js application:

process.on('SIGTERM', () => {
  log.notice('"SIGTERM" received');
  process.exit(0)
});

The good practice is to start the application by defining a start script in the package.json. For example:

{
  "scripts": {
    "start": "node server.js"
  }
}

Then define the web process in the Procfile. Example of Procfile content:

web: yarn run start

The problem with this approach is that Scalingo sends a SIGTERM signal to stop the application. But Yarn do not correctly forward this signal to the started process.

The solution is to change the content of the Procfile to directly start the Node.js process:

web: node server.js

Suggest edits

Node.js

©2024 Scalingo