How To Set Up a SSR Nuxt.js application on a LEMP stack

Introduction

Nuxt.js is a minimal framework for creating Vue.js applications with server side rendering (SSR).

In this tutorial, we will cover setting up a production-ready Nuxt.js application on a LEMP stack server. This server will run the application as service managed by PM2, and provide users with secure access to the application through an Nginx reverse proxy. The Nginx server will offer HTTPS, using a free certificate provided by Let's Encrypt.

This guide assumes that you have the following:

  • A LE(MP) stack server (we only need Linux and Nginx), configured with a non-root user with sudo privileges.
  • A domain name pointed at your server's public IP. This tutorial will use example.com throughout.

  • Your Nuxt.js application deployed or cloned on the server. This tutorial will use the following path  /var/www/example.com

Install Node.js

Our goal is to run a server side rendered application thus the first thing we need is to install Node.js on the server.

There are quite a few ways to install Node.js. For simplicity we will install the available version in the default repositories of most Linux distributions, it might not be latest version, but stable.

Depending on your Linux distribution, in order to install Node.js you will need the following.

bash

# On Debian, Ubuntu, Linux Mint:
sudo apt-get install nodejs npm
bash

# On RHEL, CentOS, you need to enable EPEL repository first.
sudo yum install epel-release

# And, then install Nodejs:
sudo yum install nodejs npm

Should you use a different distro, here you can find further instructions.

Build Nuxt.js

The first thing we need to do, if we haven't done it yet, is installing all the node modules defined in the project package.json.

Secondly, with the modules installed, we can build the app and start the server with the Nuxt.js built-in commands: nuxt build and nuxt start.

bash

cd /var/www/example.com

# Install node modules.
npm install

# Build app.
./node_modules/.bin/nuxt build

# Start the server.
./node_modules/.bin/nuxt start

If everything is correct you should get something like the following:

OPEN http://localhost:3000

This means the node server is running and listening on the port 3000 but there is a problem. Running a Node.js application in this manner will block additional commands until the application is killed by pressing Ctrl-C. In addition, if we close the terminal session the server will stop.

In order to overcome this problem we will run the server as background service using a process manager called PM2.

Manage the app with PM2

PM2 a process manager for Node.js applications. PM2 provides an easy way to manage and daemonize applications (run them in the background as a service).

We can easily install it with npm

bash

sudo npm install -g pm2

The -g option tells npm to install the module globally, so that it's available system-wide.

PM2 is simple and easy to use. We will cover a few basic uses of PM2. For further documentation visit the official page.

The first thing you will want to do is use the pm2 start command to run your application in the background:

bash

cd /var/www/example.com
pm2 start ./node_modules/nuxt/bin/nuxt-start --name my-app-server

This will start and add your application to PM2's process list with the name my-app-server, which is outputted every time you start an application or show the process list with pm2 list:

How To Set Up a SSR Nuxt.js application on a LEMP stack

Applications that are running under PM2 will be restarted automatically if the application crashes or is killed, but an additional step needs to be taken to get the application to launch on system startup (boot or reboot). Luckily, PM2 provides an easy way to do this, the startup subcommand.

The startup subcommand generates and configures a startup script to launch PM2 and its managed processes on server boots:

bash

pm2 startup systemd

The last line of the resulting output will include a command that you must run with superuser privileges:

How To Set Up a SSR Nuxt.js application on a LEMP stack

Secure Nginx with Let's Encrypt

To enable HTTPS on your website, you need to get a certificate from a Certificate Authority (CA). Let's Encrypt is a free, automated, and open certificate authority.

There are several ways you can install letsencrypt and obtain the certificates. In my opinion, the manual way is the simplest.

bash

cd ~
git clone https://github.com/certbot/certbot
./letsencrypt/letsencrypt-auto certonly --manual --email admin@example.com -d example.com

This will create the follwoing directory: /etc/letsencrypt/live/example.com/ containing the following certificate files:

  • cert.pem
  • chain.pem
  • fullchain.pem
  • privkey.pem

Set up Nginx as a reverse proxy server

Now that your application is running, and listening on localhost, you need to set up a way for your users to access it. We will set up the Nginx web server as a reverse proxy for this purpose.

The first thing we need is to create a new file to store our Nginx "server blocks" configuration.

This needs to be placed in the following path /etc/nginx/sites-available and  then linked in /etc/nginx/sites-enabled:

bash

sudo touch /etc/nginx/sites-available/example.com
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com

Now open the file just created for editing:

bash

sudo nano /etc/nginx/sites-available/example.com

Copy and paste the following location blocks replacing example.com with your domain.

nginx

map $sent_http_content_type $expires {
    "text/html"                 epoch;
    "text/html; charset=utf-8"  epoch;
    default                     off;
}

server {
    listen 80;
    server_name example.com www.example.com;

    # redirects both www and non-www to ssl port with http (NOT HTTPS, forcing error 497)
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl http2;
    server_name www.example.com;

    # redirects www to non-www.
    return 301 $scheme://example.com$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com;

    gzip            on;
    gzip_types      text/plain application/xml text/css application/javascript;
    gzip_min_length 1000;

    # Logs
    access_log /var/log/nginx/example.com_access.log;
    error_log /var/log/nginx/example.com_error.log;

    # SSL
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_session_cache shared:le_nginx_SSL:1m;
    ssl_session_timeout 1440m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-SHA ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES256-SHA384 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-SHA ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-SHA384 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES256-SHA256 EDH-RSA-DES-CBC3-SHA";

    location / {
        expires $expires;

        proxy_redirect                      off;
        proxy_set_header Host               $host;
        proxy_set_header X-Real-IP          $remote_addr;
        proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto  $scheme;
        proxy_read_timeout          1m;
        proxy_connect_timeout       1m;
        proxy_pass                          http://127.0.0.1:3000;
    }

    location ~ /\.{
        access_log off;
        log_not_found off;
        deny all;
    }
}

Once you are done adding the location blocks for your applications, save and exit.

Make sure you didn't introduce any syntax errors and restart the server Nginx by typing:

bash

# Check syntax
sudo nginx -t

# Restart
sudo systemctl restart nginx

Assuming that your Node.js application is running, and your application and Nginx configurations are correct, you should now be able to access your application via the Nginx reverse proxy. Try it out by accessing your server's URL (its public IP address or domain name).