Nginx, VueJS, and Express with Docker
The last few months I have been learning VueJS when I have had some free time. While it is easy just to include Vue from a CDN and get to work directly on a web page, I have mostly focused on creating single page applications with Vue. I generally scaffold my application with webpack to get all the great features.
The one issue I did have during the development process is how all of the technologies would tie together. Webpack-dev-server generally runs on port 8080, my Node/Express backend would be running on port 3000. A basic use case is not that complicated, but when you start adding in other services things could quickly become a mess. That made me start thinking about Docker.
Granted, I know just enough about Docker to get myself in trouble, but I wanted to see if I could make a stack that was organized and easy to follow. Once the development container is up I just wanted to develop. If you would like to follow along you can find the project here:
I decided to comprise my stack of three containers; each dedicated to one specific purpose:
- Nginx will handle serving HTML or proxy requests to the development webpack server or Express API handler.
- Node/Webpack will handle running
webpack-dev-serverin development getting all the advanced features of webpack. In production we will use this container to bundle our VueJS application using the
buildcommand. Code for this container lives in the
- Node/Express will handle all of our API related calls and is contained in the
Nginx is responsible for routing all requests so it is imperative it is configured correctly. Development will require two upstream providers: webpack will handle routing requests to the
dev-webpack-server while the backend provider will route all API requests. The server in the upstream block should match the container name property set in docker-compose services.
The next step is configuring the location blocks in the Nginx config to route requests. Root requests will be routed to the dev-webpack container and API requests will be routed to dev-node.
Nginx will now properly route requests, we need to configure the upstream webpack and backend containers. The configuration is straight forward but I created a context and a Dockerfile for each container. Things are simple now but that may not always be the case. The Dockerfile will set the proper working directory and install all of the required dependencies with
yarn. Once that has been done and the container has been brought up the
yarn start command is executed for each of the containers running the
dev-webpack-server on dev-webpack and
nodemon server.js on dev-node.
It is easy to see it in action by bringing up the containers with
docker-compose -f docker-compose.dev.yaml up --build -d. Once started any changes we make to the client code will be bundled and sent to using webpacks hotload feature. Any changes to the server.js and nodemon will restart the application.
The “production” version is just a little bit different from development. Instead of Nginx forwarding requests, the web root is set to
/client/dist which is where the bundled files are placed when the fullstack-webpack container is brought up.
These changes are mirrored in the the virtual host configuration as seen below.
Now there is only one upstream host defined which points to the Node/Express backend. The web root is now just serving the static content bundled with web pack.
The production version can be launched with
docker-compose up --build -d.
My original intent was to simplify developing VueJS applications when using multiple technologies. The process works well but there are some caveats. All processes are run in their respective containers so you will need to watch the terminal output or use a tool like Kitematic to watch for lint and build errors.
A few weeks ago when I was originally writing this much of my experience was theoretical like the simple application on GitHub. In the meantime I have leveraged this prototyping utility applications at work. Since many of the things I work on will never be public facing apps I can delete the production configs, rename docker-compose.dev.yaml to docker-compose.yaml and I am ready to start developing. As noted in my conclusions, it is a bit of a switch to get used to checking Kitematic to view build errors, but you get used to it.