Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nguyenvq/262365ac65babe4d13c387e6a702b844 to your computer and use it in GitHub Desktop.
Save nguyenvq/262365ac65babe4d13c387e6a702b844 to your computer and use it in GitHub Desktop.
Deployment manual for a real-world project built with nuxt.js + koa + nginx + pm2

Example of deployment process which I use in my Nuxt.js projects. I usually have 3 components running per project: admin SPA, nuxt.js renderer and JSON API.

This manual is relevant for VPS such as DigitalOcean.com or Vultr.com. It's easier to use things like Now for deployment but for most cases VPS gives more flexebillity needed for projects bigger then a landing page.


Let's assume that you have entered fresh installation of Ubuntu instance via SSH. Let's rock:

1.Initial setup.

Depending on size of project you have two choises: make non-root user or go on with root. I'd suggest you to go with root, which is easier (except nginx part). If you have chosen non-root, check out this tutorial.

2.Install Node.js.

Using nvm or directly:

sudo apt-get install python-software-properties
curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash -
sudo apt-get install nodejs

3.Directory structure

It's actually up to you, depending on type of your project, but I usually use this structure:

  • user dir (ex., /root)
    • config.yml (pm2 config)
    • app (project name)
      • client (nuxt.js files)
      • server (API server files)
      • public (static content)

Fill server dir with your API code, in my case it's nodejs + koa2 + mongoose stack. Place at least favicon and robots.txt to public dir.

4.Upload nuxt.js bundle.

If you use enviroment variables (I hope you are), open package.json file and change build script to "build": "NODE_ENV=production nuxt build -a", On production build, node.js will pass production flag to your nuxt.config.js. For example, you can make API URL different for dev and prod enviroments and more. Very usefull. (for Windows users, see cross-env package).

Now it's time to make production build. Run npm run build on your local machine. I don't recommend to build nuxt.js on production server, because it eats lots of memory and causes up to minute of downtime. Take package.json, nuxt.config.js and .nuxt dir and copy them via sftp (or pull from git) to client dir.

Move to your client directory on the server and install production dependencies (in most cases you need only nuxt package): npm i -—production.

5.Set up PM2

PM2 is a process manager for node.js. I suggest it for newbies, but you can also try things like Passenger. Install PM2 and create config file in your user root dir: config.yml. See config example below.

6.Set up Nginx

Install nginx:

sudo apt-get update
sudo apt-get install nginx

(If you using root user, you have to set right permissions for project dirs to make it work with static files. Or just change www-data to root in nginx.conf. Duno, it works for me.). Then edit config file /etc/nginx/sites-available/default, see config example below. Don't forget to sudo nginx -s reload after every nginx modification.

If you have already connected domain to your project, it's super easy to set up https (and http2). See this tutorial for installing certbot.

7.Fire it up!

Move to dir that contains your pm2 config file and run pm2 start config.yml –-env production. Yay, everything should work for now, but it doesn't. Run pm2 logs to see errors. This manual is complicated for a newbie and imperfect itself, so you will probably have to try several times. But it's worth it.


Contributions to this manual are appreciated.

---
apps:
- name: client # process name. You will use it to make commands such as pm2 restart client
script: node_modules/nuxt/bin/nuxt-start # path to nuxt renderer from root nuxt dir. Don't touch it
cwd: /root/app/client # absolute path to nuxt dir
max_memory_restart: 250M # in case of nuxt renderer eats all memory, it will be restarted
args: "--max-old-space-size=200" # in case you running small amount of RAM
env_production:
PORT: 3000 # local port. Same port should be set in nginx config
NODE_ENV: production # in case of enviroment variables usage
# API
- name: server
script: app.js
cwd: /root/app/server
env_production:
PORT: 3001
NODE_ENV: production
server {
listen 80;
listen 443 ssl http2;
# your domain
server_name domain.com;
# static content directory
root /root/app/public;
# entry point for page renderer. Alias prop - absolute path to nuxt dir which contains prerendered nuxt pages.
# If nginx found prerendered page, it reponses. If not - real time renderer responses.
# You can programmaticaly generate pages to html files with your API and use them as cache.
location / {
alias /root/app/client/dist/$1;
try_files $uri/index.html @renderer;
}
# proxy to nuxt real time renderer.
location @renderer {
proxy_pass http://localhost:3000;
error_page 502 = /200.html;
}
# If renderer crashes, nuxt runs in SPA mode.
location = /200.html {
alias /root/app/client/dist/$1;
try_files /200.html =502;
}
# entry point for API server
location /api {
proxy_pass http://localhost:3001;
client_max_body_size 3m;
}
# entry point for SPA admin page (if you have one).
location /admin {
try_files /app/index.html =404;
}
# serve nuxt bundle with max cache life.
location ~*_nuxt(.*)$ {
alias /root/app/client/.nuxt/dist/$1;
gzip on;
gzip_comp_level 6;
gzip_vary on;
gzip_types text/css application/json application/javascript text/javascript application/x-font-ttf font/opentype;
expires max;
}
# serve static content
location ~* \.(js|jpg|jpeg|txt|png|css|ico|map)$ {
gzip_static on;
expires 30d;
}
# refirect from /path/ to /path
rewrite ^/(.*)/$ /$1 permanent;
}
# redirect for domain aliases
server {
server_name www.domain.com;
return 301 $scheme://domain.com$request_uri;
}
# placeholder if user requests your servers' IP.
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
server_name _;
location / {
try_files $uri =404;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment