A step-by-step guide to developing and deploying Vue apps with docker, part two

Vue is a great framework to develop user interfaces. It has experienced a great deal of growth in popularity since its initial release in 2014 and still continues to improve. This second part of the series will explain how to develop "in" a container and ship your app with a custom Docker workflow

Haven't read the previous article yet?
- Check out the first part of the Vue-Docker series here.
- Or if you're TL: DR - take me to the code.

Build the dev - container

Now that we have Docker installed and scaffolded our project, we can start developing. Let's begin by adding the following to the Dev.Dockerfile:

FROM node:14-alpine

WORKDIR /vue_app

EXPOSE 8080

CMD [ "npm", "run", "serve" ]
In case you wonder about the absence the  npm install command: We'll use the vue_helper container image from the first article to manage npm packages.

We can build the development container by running:

docker build  \
    -f ./dockerfiles/Dev.Dockerfile \
    -t vue_app:dev 
    vue_app

You should then receive a container named vue_app. With it, we can start development.

Note: Given you have read the previous article, you'll notice a slight difference in the build commands. Previously, we've used the STDIN method to read the content of the dockerfile. While it's generally a bit faster, it specifies no build context to the Docker daemon. If you try and replicate the STDIN way to build the development app's container, you will run into an error:

COPY failed: forbidden path outside the build context: ../vue_app/package.json

The difference, in a nutshell, is that:

  • the STDIN method uses the context of the Dockerfile ( Path: ./dockerfiles/ )
  • while the -f - method uses the context of the currently active directory ( Path: ./ )

Develop with Docker

To develop, we must keep the container's content synchronized with our local source code. This can easily be achieved by using a bind mount.

Let's launch the development container by executing the following command:

docker run \
    -v /path/to/project/vue_app:/vue_app \
    -p 8080:8080
    -it vue_app:dev

It will:

  • run a container based on the tagged image vue_app:dev and expose it on port 8080
  • sync the local working directory /path/to/project/vue_app with the container's working directory vue_app over a bind mount
  • Open an interactive session so you can keep track of your development server
If you are trying to get the container up with Vite instead of the Vue CLI,  you must currently (30.10.2021) pass a --host flag in your package.json file:
{ 
  "scripts": { 
    // ... other scripts  
    "serve": "vite --host 0.0.0.0" 
  }
}

When the container is running, the app can be accessed over a browser. Open a new tab at http://localhost:8080 and you will see the familiar Vue boilerplate. And thanks to the bind mount, modifications to the code on your local machine will be replicated straight into the container. Vue CLI will pick these up and reload the browser page.

Finally, try and change the msg property of the HelloWorld component. If the change is visible inside your browser - congratulations - you've successfully set up your own Docker dev-environment 🌟.

You can now change the application to your heart's desire.

In the repository for this article, I've created a simple to-do-list application that we can deploy later.

Prepare the app for deployment

We can already create & manage an app, as well as develop its source code using Docker. What's left to do is to make it ready for a productive environment - without the dev-server overhead.

There are several ways to do so. You could use the example from the Quick & Dirty article I wrote earlier, that is, a custom Express.js server. But why reinvent the wheel if there's a container to the rescue?

The example you find below is inspired by the official Vue 2 Docs. It works equally well with any other kind of JS framework that use Webpack or Vite.

Let's fill the remaining Deploy.Dockerfile with life by adding these commands:

# build stage
FROM node:14-alpine AS build-stage

WORKDIR /vue_app

COPY package.json ./

RUN npm install

COPY . .

RUN npm run build

# production stage
FROM nginx AS production-stage

COPY --from=build-stage /vue_app/dist /usr/share/nginx/html

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

Docker can interpret these to declare two distinct stages. This procedure is commonly referred to as 'Multi-Stage Building'.

  • The build-stage will build the app using the Node.js image
  • The production stage will use an Nginx web server image.
  • It launches a container that serves the built Vue SPA on port 80

Stages refer to one another by using the --from - flag. Like this, you can streamline your development workflow. You could, for instance, extend the above commands by adding one or several test phases.

At this point, you can also configure Nginx as per your project's requirements. Check out the official docs on Dockerhub for more information.

Once you're ready to ship your app, run:

docker build \
    -f ./dockerfiles/Deploy.Dockerfile \
    -t vue_app:production \
    vue_app

You will receive an image that includes an optimized version of your app. It can be launched anywhere Docker is installed:

docker run -p 80:80 vue_app:production

The application will then be available under http://localhost.

Let's recap:

  • We've filled the two Dockerfiles for development and deployment with commands
  • Both provide us a unique way to develop and distribute our app
  • Docker also allows us to streamline our deployment process
  • This procedure is called 'Multi-Stage Building'

Example Github Repos

I've collected all the code from the first two articles together in a Github Repos. You are welcome to use it either as a boilerplate or Proof of Concept.

GitHub - tq-bit/vue-docker-development: A boilerplate repos with three essential Dockerfiles to streamline you Vue app development project.
A boilerplate repos with three essential Dockerfiles to streamline you Vue app development project. - GitHub - tq-bit/vue-docker-development: A boilerplate repos with three essential Dockerfiles to...

Next steps

While you could use the example as-is, there's still room for improvement. So in the next and final article of the series:

  • We'll use Dockerhub to publish our app image
  • We'll create a Virtual Host on Digital Ocean and deploy our app to it
  • Instead of running each docker command separately, we'll explore the usage of docker-compose for development and deployment