Troubleshooting Node.js Deploys
Last updated December 09, 2021
Table of Contents
Your Node.js deploy failed - now what? Start with these simple steps to troubleshoot a build issue.
Check the buildpack
Is the app using the officially supported and maintained buildpack? Most of the time, the standard buildpack is the best choice - either alone, or paired with other buildpacks (such as the Heroku Ruby Buildpack).
To find out, run:
$ heroku buildpacks
To use the official buildpack, run:
$ heroku buildpacks:set heroku/nodejs
Compare Node and npm Versions
The production environment should mirror the app’s development environment as closely as possible. (Being a few patch versions off is okay.) First, check local versions:
$ node --version
$ npm --version
Then, compare the results with the package.json
engines
section. Make sure a Node version is specified.
Heroku will output which binaries (ie. node
, npm
) are used for each deploy in the build logs:
remote: -----> Installing binaries
remote: Resolving node version 12.x...
remote: Downloading and installing node 12.16.3...
remote: Using default npm version: 6.14.4
The binaries should match up with the same local versions. If they don’t, specify the matching versions in the package.json
.
Make sure the lockfile is up to date
If a lockfile is present (ie. package-lock.json
or yarn.lock
) checked into the app’s repository, make sure that the lockfile is both up to date and that the changes are checked into git.
It’s recommended to use a lockfile when third-party dependencies are referenced in the package.json
npm
In order to update the package-lock.json
, run npm install
and check in the changes to the file to git.
yarn
To update the yarn.lock
file, run yarn install
and check the changes into git.
Don’t check in generated directories
The app’s node_modules
directory is generated at build time from the dependencies listed in package.json
and the lockfile. Therefore, node_modules
(and other generated directories like bower_components
) shouldn’t be included in source control. Check with the following:
$ git ls-files | grep node_modules
If there are results, then instruct git to stop tracking node_modules
:
$ echo "node_modules" >> .gitignore
$ git rm -r --cached node_modules
$ git commit -am 'untracked node_modules'
Check for differences between development and production
Many Node applications have checks that will perform different logic based on the value of the NODE_ENV
environment variable. This is especially common when, for example, building web assets to avoid the overhead minifying JavaScript and compressing images during development.
Occasionally, this can lead to subtle bugs that will only show up when trying to deploy. If a new bug appears while deploying, check to see if it can be reproduced locally by setting NODE_ENV
to production
.
$ NODE_ENV=production npm start
Check the .gitignore
A required file may exist locally, but it’s possible to accidentally prevent it from being included in the app’s git repo by an overly broad rule in .gitignore
.
As an example, it may be better to exclude a lib
directory at the root of your application, but with the following:
lib/
git will recursively match any subdirectory named lib
, so the file js/library-name/lib/index.js
would not be included in the git repo. Fix this case by moving the slash to the front, which will only match the lib
directory in the application root directory.
/lib
Open a ticket
If none of these solutions work to fix the app in question, open a ticket with Heroku so we can help.
Common Issues
AWS Proxy Error
If builds are not completing with an error when downloading Node, there may be an issue downloading binaries. The build error may look like following:
-----> Installing binaries
engines.node (package.json): 10
engines.npm (package.json): unspecified (use default)
Resolving node version 10...
Error: Unknown error installing "10" of node
Or the error may mention this:
-----> Node.js app detected
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to lang-common.s3.amazonaws.com:443
Setting the HTTP_PROXY
or HTTPS_PROXY
environment variables in the app’s build will cause these types of failures. If the build or app does not need HTTP_PROXY
or HTTPS_PROXY
environment variables, it’s recommended to remove the variables from the app’s environment.
$ heroku config:unset HTTP_PROXY HTTPS_PROXY
If the build environment does require a proxy to be used for HTTP or HTTPS connections, set the NO_PROXY
environment variable to amazonaws.com
. This can be done by setting this in the environment (ie. export NO_PROXY=amazonaws.com
) before executing the Node process, or setting the environment variable:
$ heroku config:set NO_PROXY=amazonaws.com
Missing Modules
If a module that is included in the package.json
is missing from the build or the production app, it may have been removed by Heroku during pruning. A common error would look something like this:
internal/modules/cjs/loader.js:960
throw err;
^
Error: Cannot find module 'express'
In order create a smaller slug size for apps, the buildpack will prune out the devDependencies
from the package.json
at the end of the build, so that the slug will only include the dependencies
that are listed at runtime. If there is a dependency that is in the devDependencies
that is needed after the prune occurs, move the dependency to dependencies
, so it is not removed.
The other solution is to turn off pruning of devDependencies
altogether. To do this, set the following for npm:
$ heroku config:set NPM_CONFIG_PRODUCTION=false
If the app is using Yarn, run the following:
$ heroku config:set YARN_PRODUCTION=false
For more information about configuration package installations, visit the Node.js Support docs.
Incorrect Port Setup
Heroku apps will not bind to just any port that your app is set up with. Apps should use the Node process
to bind to a port.
When set up incorrectly, your app will deploy successfully, but the app will crash repeatedly. You may see an error like this in your logs:
heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" status=503 bytes= protocol=https
To fix this, use the process.env.PORT
variable to bind your web server to the port. If you’re using port 3000 for your local development, your code should look something like this:
app.listen(process.env.PORT || 3000);
The Node process will take the PORT
environment variable that has been set by Heroku in your app.
Install and Build Script Failures
Sometimes installing a dependency or running a build locally completes successfully, and when it gets to Heroku, the build will fail. If you run into an issue where a dependency only fails on Heroku, it’s recommended to spin up a one-off dyno to debug the script. The dyno will be created from the slug of the most recent deploy, so try to get the build to pass in order to debug in a Heroku environment - you can do this by uninstall the troublesome dependency, removing the build script from the package.json
, or making other modifications to the code to make the build pass.
Once you’ve deployed the code to a staging app, create the one-off dyno:
heroku run bash -a $APP_NAME
Running bash on ⬢ app-name... up, run.2524 (Hobby)
$
After the dyno has spun up, you can poke around and run scripts to debug. Install dependencies as needed to poke around the dyno:
$ npm install debug
up to date, audited 52 packages in 2s
found 0 vulnerabilities
If you cat
the package.json
, you’ll see that debug
has been installed. You may also have a failing build script - it can be run it manually with the verbose
flag:
$ npm run build-production --verbose