Deploying a Vue.js Single Page App (including Router) with Docker

Creating single page apps has become a more frequently requested task of web developers (like me) and deployment in containers, across zones and under monitoring, seems like a natural step.

In this post we're going to have a look at how to deploy a Vue.js SPA with docker.

Let's start off by installing the vue-cli to create a boilerplate project. Make sure you have Docker installed as well.

Installing the Vue CLI is just an npm install away:

npm install -g @vue/cli

Now we create a project with vue create projectName (I'll choose vue-docker as my project name` and pick the Manually select features configuration option.

Next, we'll arrow key down to Router, hit SPACE and ENTER. I chose to hit ENTER for the rest of the options as well.

After a quick installation, you should see output that looks a bit like this:

āš“  Running completion hooks...

?  Successfully created project vue-docker.
?  Get started with the following commands:

 $ cd vue-docker
 $ npm run serve

Now we change to the directory that our app was set up at and verify it works with:

cd vue-docker
npm run serve

You should see something similar to the screenshot below in your browser at http://localhost:8080:

To create a docker container, we'll need to create a Dockerfile that runs nginx, a high performance web server.

Dockerfile
# Create the container from the alpine linux image
FROM alpine:3.7

# Add nginx and nodejs
RUN apk add --update nginx nodejs

# Create the directories we will need
RUN mkdir -p /tmp/nginx/vue-single-page-app
RUN mkdir -p /var/log/nginx
RUN mkdir -p /var/www/html

# Copy the respective nginx configuration files
COPY nginx_config/nginx.conf /etc/nginx/nginx.conf
COPY nginx_config/default.conf /etc/nginx/conf.d/default.conf

# Set the directory we want to run the next commands for
WORKDIR /tmp/nginx/vue-single-page-app
# Copy our source code into the container
COPY . .
# Install the dependencies, can be commented out if you're running the same node version
RUN npm install

# run webpack and the vue-loader
RUN npm run build

# copy the built app to our served directory
RUN cp -r dist/* /var/www/html

# make all files belong to the nginx user
RUN chown nginx:nginx /var/www/html

# start nginx and keep the process from backgrounding and the container from quitting
CMD ["nginx", "-g", "daemon off;"]

We also need to create the nginx config files that we're referencing, nginx.conf and default.conf in the directory nginx_config:

nginx.conf
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    sendfile        off;

    keepalive_timeout  60;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}
default.conf
server {
  location / {
      root /var/www/html;
      try_files $uri $uri/ /index.html;
  }
}

The try_files line is significant, because if a file can not be found, we will serve the index.html file and let the Vue Router figure out which component to display. This is somewhat of a fallback to index.html.

Now let's build our docker container and name it vue-docker-container:

docker build -t vue-docker-container .

and lastly we can run our container with:

docker run -p 8080:80 vue-docker-container

Now you should be able to see your app in your browser again, but this time it's being run from with Docker and you are free to deploy your container where ever you please!

Modifying the Vue Router to use HTML5 history mode

In order to remove the # in the URL, we need to pass the mode option to the Vue router and set it to history like below:

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'

Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/about',
      name: 'about',
      component: About
    }
  ]
})

Deploying to Docker without building "in container"

You don't have to build your app inside the Dockerfile, you can also simply run npm run build and then copy your directory in with

COPY dist /var/www/html

in your Dockerfile. This also saves you the additional nodejs dependency, but that requires you to do one more step and if you have a team working on a project, they might use different versions to build which might add unwanted variations.

Tagged with: #docker #Nginx #Vue.js

Thank you for reading! If you have any comments, additions or questions, please tweet or toot them at me!