Nuxt & PM2: Zero Downtime Deployment

Xander Luciano
5 min readJul 18, 2018

--

Nuxt is an awesome framework for Vue.js that makes setting up an SSR site super simple. Nuxt uses a Node.js server under the hood in order to prerender webpages server side before sending them to the client.

There are many advantages to SSR, which are covered in depth in many other posts, but for this article we are going to be focusing on the deployment process of your Nuxt server.

notice: this article was originally written for Nuxt 1.x, and there is a small adjustment needed to make 2.x work. The rest of this article is still valid, and we’ll cover Nuxt 2.x at the end of the article.

PM2 is a great process manager for Node.js applications, with many great features including load balancing through “cluster mode.” Cluster mode creates multiple instances of a Node.js process in order to allow applications to be scaled across multiple CPUs without any code modifications.

Besides the performance advantage of utilizing multiple CPUs, cluster mode also allows for a zero downtime deployment by reloading the server process one instance at a time. Thus if you have 4 instances running, you can reload the server one instance at a time, and while one instance is reloading, the other 3 instances will keep the server alive. Thus you can update your web server can be updated without being taken offline for even a second.

Setting up and using PM2 cluster mode is extremely easy for most node applications, however we run into some issues attempting to use cluster mode with our nuxt server. Let’s explore why this occurs.

First we start by creating a single instance of our nuxt server with the following command:

$ pm2 start npm --name MyAppName -- start
Single instance of Nuxt server with PM2

But we will quickly run into issues when trying to start multiple instances.

For example, if we run that command twice, we’ll run into errors.

Error when running multiple instances

Now while this is by default running in PM2’s fork mode, even if we were to specify running in cluster mode, we will still run into errors.

What if we only use 1 instance and attempt to reload the server? Well due to how Nuxt works under the hood, if we attempt to rebuild the server while it’s running, we’ll hit 404 errors after the build files are removed and rehashed for the new build. We also run into issues if files in our static folder are changed.

So if we can’t just rebuild and reload a server, and we can’t run multiple instances, then how do we achieve a zero downtime deployment?

Properly Configure PM2 Cluster Mode

We were running into issues previously because we were attempting to spawn multiple instances of our server in the “wrong way”. In our instance we are having PM2 execute `npm run start` which essentially is starting multiple instances of npm script executions, and not properly launching our server directly. We can see this in our console after starting an instance:

[PM2] Starting /usr/bin/npm in fork_mode (1 instance)

As you can see, we’re starting npm and then telling npm to start our server through it’s command line arguments. Instead of this, we should be using PM2 to call the server’s start script directly. Starting a Nuxt server is done with the following command:

Nuxt start

However, if we try to tell PM2 to start Nuxt, it won’t work. Instead, we need to point directly to the script Nuxt calls, which is located in

./node_modules/nuxt/bin/nuxt-start

Now starting PM2 with a script from the node_modules folder is going to be tedious, there has to be a better way. Thankfully, there is! PM2 allows us to create an Ecosystem File, which is basically a configuration file for our PM2 commands. We can specify properties such as the script to run, the working directory, how many instances to spawn, and an alias name. You can see all available properties here.

So now that we know what script we need to run, and we know what settings we need to execute that script with, we can create the ecosystem file by running the following command:

pm2 init

We’ll then configure this file as such:

module.exports = {
apps : [{
name : 'MyAppName', // App name that shows in `pm2 ls`
exec_mode : 'cluster', // enables clustering
instances : 'max', // or an integer
cwd : './current', // only if using a subdirectory
script : './node_modules/nuxt/bin/nuxt-start', // The magic key
}]
};

You may notice I’m specifying a current working directory (cwd). I’m doing this because this is what allows us to swap our server code behind the scenes without running into the limitations caused by Nuxt.

We do this by creating a file structure like this:

/MyAppName
|--/releases
|----/v1.0.0
|----/v1.1.0
|----/v1.2.0
|----/...
|--/current -> /releases/v1.2.0 (symlink)
|--ecosystem.config.js

To start our server, we just need to run the following command from within the /MyAppName directory:

pm2 start

Now to deploy we just clone the latest version of our repo to a new folder under ‘/releases’, build our new version (or alternatively, you can use a CI/CD service to build that version ahead of time, and directly upload those build files to the server). Then we just update the symlink from /releases/v1.2.0 to /releases/v1.3.0 and run:

pm2 reload MyAppName
Multiple instances of Nuxt Server

Success! You’re now able to spawn multiple instances of your Nuxt server for better performance (on multi threaded systems), AND take advantage of zero downtime deployments to keep your website online during updates.

Hopefully this helps you make the most out of your project!

2020.07.27 UPDATE For Nuxt.js 2.x

The location of the startup script for the nuxt module has moved!
It is no longer in the same location, and has 1 other minor adjustment needed.

script    : './node_modules/nuxt/bin/nuxt-start',

But thanks to a comment from Mikhail Bykov there is a simple fix!

https://medium.com/@tetraset/in-new-version-nuxt-file-is-here-node-modules-nuxt-bin-nuxt-js-dev-mode-21e644b61f68 (his original comment)

module.exports = {
apps : [{
name : 'MyAppName', // App name that shows in `pm2 ls`
exec_mode : 'cluster', // enables clustering
instances : 'max', // or an integer
cwd : './current', // only if using a subdirectory
// These are our updated properties
script : "./node_modules/nuxt/bin/nuxt.js",
args : "start",
}]
};

Note the addition of the args property with a value start. We need to tell Nuxt’s code what task to run now, and this accomplishes that in a clean and simple way. Now you can upgrade your old 1.x Nuxt projects to 2.x, without worrying about losing your zero downtime deployment process.

--

--

Xander Luciano

Creating innovative open platform solutions through open source software.