Motivations

If you are like me, then you easily get excited by the latest technologies in web development. Docker1 is one tool that skyrocketed in popularity, and I am very interested in learning it. Docker is often seen as a lightweight alternative to virtual machines. I am currently working on an API written in Node.js using MongoDB and Redis. Dependencies always seem to start piling up pretty quickly on any project, and that means you have to install and manage local installations for each of them. I really liked the idea of using containers for local development because it meant I wouldn’t have to install Mongo and Redis on my computer. I also often forget to make sure both are running before I start my Node server, so just running docker-compose up to start Node, Redis and Mongo all at once is very useful.

Getting Started with Docker

Installing Docker is very straightforward with the Docker Toolbox, and they have fantastic documentation for getting started. You can read through the docs to get familiar with writing Dockerfiles and working with Docker Compose. It took a little while for Docker to click with me. I did not really understand what a container was or what Docker was doing. I found the Docker training videos to be incredibly valuable. The explanation of Docker in the first video finally helped me get it.

The Dockerfile

After becoming acquainted with Docker you’ll want to write a Dockerfile for your application. I use nodemon during development of any Node app. It runs your Node server and watches for any changes to your source code. It then reloads the server when a change is detected. I wanted to create an image that would install nodemon, and use it to start my app.

This is what my Dockerfile looks like:

# latest official node image
FROM node:latest

MAINTAINER Jonathan Foster <jxf9099@rit.edu>

# use nodemon for development
RUN npm install --global nodemon

# use cached layer for node modules
ADD package.json /tmp/package.json
RUN cd /tmp && npm install
RUN mkdir -p /usr/src && cp -a /tmp/node_modules /usr/src/

# add project files
WORKDIR /usr/src
ADD . /usr/src

EXPOSE 3000

CMD ["nodemon", "-L", "/usr/src/bin/www"]

You can also see my Docker Compose file, it links the Mongo and Redis containers to my application container allowing me to easily connect to them.

Why Isn’t Nodemon Reloading?

I wrestled with this process for a few days. Initially the last line of my Dockerfile looked like this:

CMD ["nodemon", "/usr/src/bin/www"]

The Dockerfile would build my image and when the container started I could see that nodemon had started. However, when I made a change to the Node application’s source the server did not reload. When I ran into this issue, I of course Googled my problem and found this old GitHub issue. There were a lot of suggestions and varying opinions throughout the thread that I had to sift through. I eventually saw that passing -L, which means “legacy watch,” to the nodemon command could resolve the problem. It turns out this is the documented suggestion in the README of nodemon. I suppose I should have done a bit of reading first because searching Google brings you to many old blog posts or GitHub issues that often led to dead ends.

Legacy Watch Concerns

So what does “legacy watch” actually mean? It tells nodemon to use polling, the method that older versions of nodemon used to detect changes in your source code. Polling refers to the process of continuously checking the status of something, and in this case it is a file containing text, our code. Nodemon will be constantly checking the files for a change. Imagine a classroom where the teacher asks every individual student if they have a question after explaining a concept. They take the time to check with each student in the room instead of having a student raise their hand when they have a question. The nodemon README recommends to only use -L as a last result because nodemon will then be polling every file in your project. That is a pretty big blow to performance, and you may experience high CPU activity with larger codebases. Since version 1.4.2 nodemon has used the library Chokidar for change detection in the code instead of its own polling implementation. In the Chokidar README on GitHub they do a great job of explaining why you would use it and how it works. It is far more efficient than polling your entire codebase.

  1. Docker was version 1.10 at the time.