loading...
Cover image for Dockerise your Nuxt SSR App like a boss! (A True Vue Vixens story) [Part 1]
Front-End Foxes

Dockerise your Nuxt SSR App like a boss! (A True Vue Vixens story) [Part 1]

superdiana profile image Super Diana Updated on ・5 min read

Intro 💅[THIS IS A VERY OUTDATED ARTICLE THAT I WILL REVISE SOON]

Our awesome friend Natalia Tepluhina coded our Vuetiful Nuxt SSR website: (https://vuevixens.org). We are all super happy and proud about it. She can totally guide you through all the development details, I will only take our website to show you how easy it is to pack it in Docker and talk from the DevOps perspective of Vue Vixens.

Before getting our hands dirty (or not!), I'd like to quote the official documentation of Vue.js on Docker and an awesome take on DevOps culture:

Effects of DevOps
The adoption of DevOps culture, tools and agile engineering practices has, among other things, the nice effect of increasing the collaboration between the roles of development and operations. One of the main problem of the past (but also today in some realities) is that the dev team tended to be uninterested in the operation and maintenance of a system once it was handed over to the ops team, while the latter tended to be not really aware of the system’s business goals and, therefore, reluctant in satisfying the operational needs of the system (also referred to as “whims of developers”).

So, delivering our Vue.js app as a Docker image helps reducing, if not removing entirely, the difference between running the service on a developer’s laptop, the production environment or any environment we may think of.

No better words to express the need for all of us to be part of it. Without this meaning you having to change your specialty, being aware of the "other side" of the process can only be to your advantage. Having said that, I'd like to quote the Vue.js cookbook one more time:

So, creating a Docker image for our Vue.js app is a good choice here because that would represent our final build artifact, the same artifact that would be verified against our continuous delivery pipeline and that could potentially be released to production with confidence

This is obviously when applicable. Docker like fries, doesn't always work with everything. Assuming it is the case that going forward this way applies to you, or that you're interested on how to get it done... let's get started!.


Let's get it started 🎵

⚡️Pre-requisites:

Provided that you have your app ready, you need the following installed in your local machine/server:

⚡️Folder structure

I created a different folder structure for this case. My local folder for the Vue Vixens website is "website". Inside that folder, I created a sub-folder and called it app and moved the whole app in its entirety to that folder.

Then, created another sub-folder in website and called it nginx. This structure is to help us build a whole image of our app with its own nginx container so we only worry about starting and stopping the app without having to configure nginx and pm2 separately to keep it alive.

This is how my folders look like.
folders

⚡️Creating a Dockerfile

In our app folder, create a file and name it Dockerfile. This a text document that contains all the commands a user could call on the command line to assemble an image. And it has no extension.

dockerfile

Our Dockerfile:

FROM node:10.7

ENV APP_ROOT /src

RUN mkdir ${APP_ROOT}
WORKDIR ${APP_ROOT}
ADD . ${APP_ROOT}

RUN npm install
RUN npm run build

ENV HOST 0.0.0.0

Understanding our Dockerfile

  • In this file we specify the node version we want our container to run. That's entirely up to you. latest is also a valid tag.
  • It is also specified the app root directory and then the commands we run to build our app.
  • The host is set to 0.0.0.0 to give full external access to the app container.

⚡️Docker Compose

According to the official Docker docs, Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application's services. Then, with a single command, you create and start all the services from your configuration. (...) Run docker-compose up and Compose starts and runs your entire app. (Now we know why i love compose so much! 💁‍♀️)

Our compose file is located in our root website folder:

folders

Let's take a look to our docker-compose.yml

version: "3"

services:
  nuxt:
    build: ./app/
    container_name: vuevixens-website
    restart: always
    ports:
      - "3333:3333"
    command:
      "npm run start"

  nginx:
    image: nginx:1.13
    container_name: vuevixens-nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx:/etc/nginx/conf.d
    depends_on:
      - nuxt

Understanding our docker-compose file:

  • Version: We are using the latest compose version which is 3
  • Services: Specify the set of services our app is composed of. In this case: nuxt.
    • Build: These are configuration options that are applied at build time. Can be specified either as a string containing a path to the build context, or an object with the path specified under context and optionally dockerfile and args.
    • Container_name: Your container's name.
    • Restart: Restart policy to apply when a container exits (default "no").
    • Ports: Expose ports. Either specify both ports (HOST:CONTAINER), or just the container port (a random host port will be chosen).
    • Command: After we specified in our Dockerfile the install and build commands, then we pass the start command to run our app.
  • Nginx: Another service we use, in this case it will be our server.
    • Image: Tag or partial image ID. Can be local or remote - Compose will attempt to pull if it doesn't exist locally. In this case we specified our nginx version. The tag :latest works as well.
    • (Skipping repeated concepts, and going straight to..)Volumes: Specifies the volumes to be created as part of your app. This is what we will use our nginx folder for, to inject our nginx config to the container.
    • Depends_on: Specifies the volumes to be created as part of your app. In this case our nginx is tied to our nuxt app.

⚡️Configuring nginx

We are almost ready. Our last step is to configure our nginx. By default, our app will be running on port 3000, we will use nginx as a reverse proxy.

Lets go to our nginx folder and create the file: default.conf with the following contents:

server {
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://vuevixens-website:3333;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

In location we connect directly with our app container, so whatever you call your app container has to have the same name in this file.

- We are ready to rock 🔥

Running your app like a boss 💪

Commit and clone your repo in your server. (The following instruction applies if you're running from your own computer)

  • Cd to the website folder
  • Run docker-compose up --build -d and that's it!. The first time you run it, all the necessary files will be pulled from the docker repositories so it might take some time depending on your connection.

If you want to check everything went alright, run docker ps and you should see something like this:
console

That's it! hope you enjoyed the experience as much as I did! - A second part on adding a let's encrypt certificate is coming soon!

Discussion

pic
Editor guide
Collapse
aitchkhan profile image
Haroon Khan

You can do:

docker-compose up --build -d

instead of:

docker-compose build && docker-compose up -d
Collapse
superdiana profile image
Super Diana Author

You're totally right! :) Cheers!

Collapse
kiritchoukc profile image
KiritchoukC

Thanks for the article, it was very useful !

However...

When executing

docker-compose up --build -d

I got an errno 137 at the nuxt building step.

If anyone encounters the same issue, I resized my DigitalOcean droplet for more RAM.

Maybe there's a better solution but that's how I got it working.

Collapse
superdiana profile image
Super Diana Author

Have you checked that you add the --prod tag when you run the nuxt build?

Collapse
kiritchoukc profile image
KiritchoukC

I didn't! I'll try this. Thanks

Collapse
ar3s profile image
Oliver

Hi Diana,
I've follow your tutorial but since on port 80 I already have Apache so I've put Nginx on port 8080.
Everything is pretty straight to your file except for these lines:

docker-compose.yml
ports:
- "8080:8080"

nginx default.conf
server {
listen 8080;

Everything compiles okay but when i visit the site:8080 Nginx returns me 502 Bad Gateway.
Can you help me? Thanks! :)

Collapse
jessewhazel profile image
Jesse Hazel

For anyone who comes across this error, check to make sure your ports specified in docker-compose for the Nuxt container actually match what port that container is running internally. Normally Nuxt will default to port 3000 but Vue Vixens has changed that to 3333 in their config: github.com/VueVixens/website/blob/....

That change is just sort of assumed and not highlighted in the article since it's an existing project. So if you just fired up a new Nuxt project or imported an existing one that has the standard config, change your port numbers in docker-compose.yml and default.conf files.

Collapse
nikolaysalinder profile image
nikolaysalinder

I have the same issue!

Collapse
iamwwc profile image
weichaowu

Should we removenode_modules from docker images after npm run build?

node_modules so big(300MB).. I don't think it's a good idea if we build images with node_modules but I don't know How to solve.

For Vue SSR, we start process from server.js

even though we bundle all codes into dist folder, server.js not bundled.

If I remove all node_modules, server.js cannot works anymore because it require('express')

Collapse
sobolevn profile image
Nikita Sobolev

Great article! Love to see more nuxt + docker setups out there.

I would also like to share our solution: github.com/wemake-services/wemake-...
It also features gitlab-ci to test and build your apps.

Collapse
superdiana profile image
Super Diana Author

Hi Nikita! that's awesome! thanks a lot!

Collapse
zshahab91 profile image
zshahab91

Hi
I go to step by step your doc....I have a question after run docker-compose build && docker-compose up -d in terminal.... how to run my docker? what is server for show my web in localhost?

Collapse
superdiana profile image
Super Diana Author

Literally 'localhost' since it's configured to show on port 80 :) sorry for the delay!

Collapse
zshahab91 profile image
zshahab91

when iI want run docker-compose build && docker-compose up -d in my project ( my project consist of Nuxt JS and CoreUI : github.com/muhibbudins/nuxt-coreui ) I can't create docker and received Error :
ERROR: Service 'nuxt' failed to build: The command '/bin/sh -c npm run build' returned a non-zero code: 1

Can You help me for Dockerize Nuxt js with Core UI please?

thanks

Thread Thread
superdiana profile image
Super Diana Author

Sure let me look at this!

Thread Thread
zshahab91 profile image
zshahab91

I can't find you in slack...my username in your slack is zshahab91

Thread Thread
superdiana profile image
Super Diana Author

github.com/alphacentauri82/nuxt-co...

I dockerised this, check it out for reference.

Collapse
johanneslichtenberger profile image
Johannes Lichtenberger

Hi Diana,

can you help me with this?

dev.to/johanneslichtenberger/oauth...

I have a branch for the OAuth2 authentication:

github.com/sirixdb/sirix-web-front...

I have added a detailed description how to setup the SirixDB HTTP-Server and Keycloak, but the problem currently is related to Docker Networking:

GET http://keycloak:8080/auth/realms/sirixdb/protocol/openid-connect/auth?redirect_uri=http%3A%2F%2Fkeycloak%3A3005&response_type=code&client_id=sirix net::ERR_NAME_RESOLUTION_FAILED
commons.app.js:1001 Uncaught (in promise) Error: Network Error
    at createError (commons.app.js:1001)
    at XMLHttpRequest.handleError (commons.app.js:536)
createError @ commons.app.js:1001
handleError @ commons.app.js:536
Promise.then (async)
login @ index.js:42
click @ index.js:82
invokeWithErrorHandling @ commons.app.js:15696
invoker @ commons.app.js:16021
invokeWithErrorHandling @ commons.app.js:15696
Vue.$emit @ commons.app.js:17719
handleClick @ vendors.app.js:15399
invokeWithErrorHandling @ commons.app.js:15696
invoker @ commons.app.js:16021
original._wrapper @ commons.app.js:20744

You might find the problem in my docker-compose.yml file. I'm relatively new to writing Docker and docker-compose files.

Collapse
michaelgv profile image
Mike

That’s neat - however, what about something like Ansible containers? Making it non-dependant on docker, instead any runtime/container Engine?

Collapse
superdiana profile image
Super Diana Author

Definitely interesting options. There are so many different approaches! - I guess I can just start a series of deployment articles for frontenders. You gave me a great idea! 🤘

Collapse
michaelgv profile image
Mike

I’d love to collaborate if you want to do a series or anything? 👍

Thread Thread
superdiana profile image
Super Diana Author

Hey Michael! are you on the Dev.To Chat? let's do something together!

Thread Thread
michaelgv profile image
Collapse
ejwaibel profile image
Erik Waibel

Love it! I've also used Docker for spinning up a small server that can load the files produced from running yarn generate so I can test out that the files generated will load properly in a web server environment.

Collapse
sorin89 profile image
Sorin

Great article, thanks! Can I change the service name from nuxt to something else?

Collapse
superdiana profile image
Collapse
audente profile image
Juan

Great article, I was stuck trying to integrate Nuxt with Nginx and Docker and your guide made it really simple.
Thanks!

Collapse
erss400 profile image
Mbiarrambang Alain

Nice article. But am having some errors after running
docker-compose up --build -d command;

ERROR: for jekalowa-account-nginx Cannot start service nginx: driver failed programming external connectivity on endpoint jekalowa-account-nginx (12ff3d36f94d71dda7bccb83efcbcdfac4df65c420d9a04124b23dc52c8c6e79): Error starting userland proxy: listen tcp 0.0.0.0:80: bind: address already in use

What should i do please?

Collapse
farnetani profile image
Arlei F. Farnetani Junior

Cool! Thanks a lot!!!

Collapse
_shirish profile image
Shirish

Greate Writeup! Diana.

Running docker in local dev environment is good, but if you can add a section on deploying the dockerised app in production environment then that would be wonderful.

Collapse
superdiana profile image
Super Diana Author

Thank you very much for reading! there's an article coming about the CD/CI side, I promise! but for now you can actually pull your repo in your production server and run the commands to build and start your containers.

Collapse
vannsl profile image
Vannsl

Hi,

thanks for the article! Does this setup include HMR of the nuxt app? Changes within the .vue files, but also in the nuxt config ?

Best
Vanessa

Collapse
blowsie profile image
Sam Blowes

nuxt-start is almost 10 times smaller than nuxt since it doesnt have build tools , how can we use nuxt-start in production with docker?

Collapse
eskimm profile image
Alan Redzepagic

Is it straight forward to add SSL with certbot to Nginx in this case ?

Collapse
superdiana profile image
Super Diana Author

There are many ways to do it. I guess whatever suits you best :)

Collapse
pvautour profile image
PVautour

I followed this tutorial and it worked better for my needs. Check it out if you are having problems.

jonathanmh.com/deploying-a-nuxt-js...

Collapse
jonathanalumbaugh profile image
Jonathan Alumbaugh

Q: why use nginx instead of the built-in server?

Collapse
steeve profile image
Collapse
superdiana profile image
Super Diana Author

Yikes, i haven't updated this article. Many things have changed ever since!!