Troubleshooting Node.js Deploys
Last updated December 04, 2024
Table of Contents
This article only applies to Cedar-generation apps, which use classic buildpacks.
Your Node.js deploy failed, now what? Start with these 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
You want the production environment to 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 engines
section in your package.json
. Make sure that a Node version is specified.
Heroku outputs which binaries (ie. node
, npm
) are used for each deploy in the build logs.
remote: -----> Installing binaries
remote: Resolving node version 22.x...
remote: Downloading and installing node 22.11.0...
remote: Using default npm version: 10.8.1
If the binaries don’t match up with the same local versions, specify the matching versions in package.json
.
Make Sure That the Lockfile Is Up to Date
If a lockfile is present and checked into the app’s repository, such as package-lock.json
or yarn.lock
, make sure that the it’s up to date and checked into Git.
We recommend using a lockfile when the package.json
references third-party dependencies.
npm
To update the package-lock.json
, run npm install
, and then check the changes into Git.
yarn
To update the yarn.lock
file, run yarn install
, and then 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. node_modules
and other generated directories like bower_components
don’t belong in source control. Check with:
$ git ls-files | grep node_modules
If you get results, 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 perform different logic based on the value of the NODE_ENV
environment variable. These checks are especially common when, for example, building web assets and compressing images during development.
Occasionally, checks can lead to subtle bugs that only show up when trying to deploy. If a new bug appears while deploying, check to see if you can reproduce it locally by setting NODE_ENV
to production
.
$ NODE_ENV=production npm start
Check the .gitignore
A required file can exist locally, but it’s possible that an overly broad rule in .gitignore
accidentally blocks it from inclusion in the app’s Git repo.
As an example, sometimes it’s best to exclude a lib
directory at the root of your application, but with:
lib/
Git recursively matches any subdirectory named lib
, so the file js/library-name/lib/index.js
isn’t included in the Git repo. Fix this case by moving the slash to the front, which only matches 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 aren’t completing with an error when downloading Node, sometimes there’s an issue downloading binaries. The build error can look like:
-----> Installing binaries
engines.node (package.json): 22
engines.npm (package.json): unspecified (use default)
Resolving node version 10...
Error: Unknown error installing "22" of node
Or the error can mention:
-----> 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 cause these types of failures. If the build or app doesn’t need HTTP_PROXY
or HTTPS_PROXY
environment variables, we recommend removing the variables from the app’s environment.
$ heroku config:unset HTTP_PROXY HTTPS_PROXY
If the build environment requires a proxy for HTTP or HTTPS connections, set the NO_PROXY
environment variable to amazonaws.com
before you execute the process. Another option is to set the config var:
$ heroku config:set NO_PROXY=amazonaws.com
Missing Modules
If a module included in the package.json
is missing from the build or the production app, it’s possible that Heroku removed it during pruning. A common error looks something like:
internal/modules/cjs/loader.js:960
throw err;
^
Error: Cannot find module 'express'
To create a smaller slug size for apps, the buildpack prunes out the devDependencies
from the package.json
at the end of the build so that the slug only includes the dependencies
listed at runtime. If there’s a required dependency in the devDependencies
after the prune occurs, move the dependency to dependencies
so that the buildpack doesn’t remove it.
The other solution is to turn off pruning of devDependencies
altogether by setting a config var:
$ heroku config:set NPM_CONFIG_PRODUCTION=false
If the app uses Yarn, run:
$ heroku config:set YARN_PRODUCTION=false
For more information about configuration package installations, visit the Node.js Support docs.
Incorrect Port Setup
Heroku apps don’t bind to just any port that your app is set up with. Apps use the Node process
to bind to a port.
When set up incorrectly, your app deploys successfully, but it crashes repeatedly. You can see an error like this one in your logs.
heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" status=503 bytes= protocol=https
To fix this issues, use the process.env.PORT
variable to bind your web server to the port. If you use port 3000 for your local development, your code looks something like:
app.listen(process.env.PORT || 3000);
The Node process takes the PORT
environment variable that Heroku set in your app.
Install and Build Script Failures
Sometimes installing a dependency or running a build locally completes successfully, but when it gets to Heroku, the build fails. If you run into an issue where a dependency only fails on Heroku, we recommend spinning up a one-off dyno to debug the script. The dyno is 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 make the build pass by uninstalling the troublesome dependency, removing the build script from the package.json
, or making other modifications to the code.
After you deploy the code to a staging app, create the one-off dyno.
heroku run bash -a example-app
Running bash on ⬢ example-app... up, run.2524 (Basic)
$
After the dyno has spun up, you can poke around and run scripts to debug. Install dependencies as needed.
$ npm install debug
up to date, audited 52 packages in 2s
found 0 vulnerabilities
If you cat
the package.json
, you see that debug
installed. You can also have a failing build script, but you can run it manually with the verbose
flag.
$ npm run build-production --verbose