Running a Node.js application can be as trivial as node index.js, but running it in production and keeping it running are completely different. Whenever the application crashes or the server reboots unexpectedly, we want the application to come back alive.
There are several ways we can properly run a Node.js application in production. In this article, I will be talking about how to deploy one using pm2 in an AWS EC2 instance running Amazon Linux.
Spin up an EC2 instance of your liking. Consider the load your server will be going through and the cost. Here you can get a pricing list for different types of instances:
Choose Amazon Linux AMI. This is a free offering from Amazon.
The Amazon Linux AMI is a supported and maintained Linux image provided by Amazon Web Services for use on Amazon Elastic Compute Cloud (Amazon EC2). It is designed to provide a stable, secure, and high performance execution environment for applications running on Amazon EC2. It supports the latest EC2 instance type features and includes packages that enable easy integration with AWS. Amazon Web Services provides ongoing security and maintenance updates to all instances running the Amazon Linux AMI. The Amazon Linux AMI is provided at no additional charge to Amazon EC2 users.
This will install version 10 of Node.js. If you want to install a different version you can change the location.
We will run our application using pm2. Pm2 is a process manager for Node.js. It has a lot of useful features such as monitoring, clustering, reloading, log management, etc. I will discuss some of the features we will use and configure in our application.
The features I find most noteworthy:
Clustering — runs multiple instances of an application (depending on configuration, in our case we will use number of cores to determine this)
Reloading — reloads applications when they crash or the server reboots.
sudo npm install pm2@latest -g
Generate a pm2 startup script:
This will daemonize pm2 and initialize it on system reboots.
What this configuration tells pm2 is, run the application and name it My App. Run it using the script dist/index.js. Spawn as many instances of the application according to the number of CPUs present.
Mind the NODE_ENV environment variable. This has several benefits when running an express application. It boosts the performance of the app by tweaking a few things such as (Taken from express documentation):
This command reloads the application with production environment declared in the ecosystem file. This process is also done with zero downtime. It compares the ecosystem configuration and currently running processes and updates as necessary.
We want to be able to write up a script for everytime we need to deploy. This way, the app is not shut down and started again (which a restart does).
When our application is up and running, we have to save the process list we want to respawn for when the system reboots unexpectedly:
We can check our running applications with:
Monitor our apps:
Let’s create a handy script to deploy when there is a change:
npm run build
pm2 reload ecosystem.config.js --env production
Make the file executable:
chmod +x deploy.sh
Now, every time you need to deploy changes, simply run:
Create an EC2 instance running Amazon Linux
Update packages (might include security updates).
Install the desired Node.js version.
Use a process manager to run the application (such as pm2).
Use deploy keys to pull code from the source repository.
Create an ecosystem configuration file so that it is maintainable in the future.
Create a deploy script so that it is easy to run future deployments.
Run the deployment script whenever there is a change to be deployed.
Congratulations! Your application is up and running.
There are several other ways to achieve the same end goal, such as using forever instead of pm2, or even using Docker instead and deploy to Amazon ECS. This is a documentation of how I deploy Node.js applications in production if running them on EC2 instances.
When your deployments become more frequent, you should consider a CI/CD integration to build and deploy whenever there is a change in the source code.
Make sure you monitor and keep an eye on your server’s resource usage.
Last but not least, make sure you have proper logging in your application. I cannot stress enough how important proper logging is.
At Monstarlab, we are using SonarQube to gather metrics about the quality of our code. One of the metrics we were interested in is code coverage. However, just running sonar-scanner on the project will not upload the coverage data to our instance of Sonar...
CI Pipelines help improve efficiency by automating complex workflows. With GitHub Actions, it's easier than ever to bring CI/CD directly into your workflow from your repository. Put together with the CI pipeline, a series of automated workflows that help ...