I'm not lazy. I'm efficient.

Nate Clark




Docker + Node.Js = Happy Panda

Nate ClarkNate Clark

Docker + NodeJs = Happy Panda

Running your nodejs app locally with Docker. A docker newbie's guide to docker tinkering.

Install Requirements

  1. Virtualbox
  2. Docker Toolbox
  3. A runnable nodejs project with a valid package.json (including an npm start script)

Getting started

TLDR; Resulting code here: docker-nodejs-example

The Docker Machine

The "docker machine" is a virtualbox VM that listens for docker commands. It's sole purpose
is to manage docker images and containers. In most cases there will only be one of these in a developer's environment.
Also note that only one machine can be enabled at a time.

Create your docker machine
  1. Run docker-machine create my-machine --driver virtualbox
    • docker-machine is the docker toolbox cli for manipulating docker machines.
    • create is the create machine command.
    • my-machine is the name of your docker machine. For the purposes of this tutorial our machine name will be "my-machine". If you open up Virtualbox you will now see a box that corresponds with the name you have provided. Please note that tinkering with this box in Virtualbox is not a good idea. All machine manipulation should be done via the docker-machine cli.
    • --driver virtualbox defines the communication driver so all future docker-machine commands can properly communicate with your docker machine's VM.
  2. Run docker-machine ls
    • This command lists out all of the docker-machines you have created.
    • You should now see my-machine in this list and you should also see that it is currently running.
  3. Run eval $(docker-machine env my-machine)
    • This command will register the DOCKER_* environment variables with your current terminal session.

Note the following commands for future use:

The Dockerfile

A Dockerfile is a yaml document that contains all the commands a user could call on the command line to assemble an image. Using docker build users can create an automated build that executes several command-line instructions in succession.

Create your Dockerfile
  1. Navigate to your node.js application directory
  2. Run touch Dockerfile
  3. Paste the following into your Dockerfile:
# Defines the base or platform image for your custom docker image. E.g. node, node:0.12.7
FROM node 

# Create a directory to hold your application
RUN mkdir /src  

# Add package.json to the /src directory so [npm install] is 
# only ran when changes to this file are detected.
ADD package.json package.json 

# Run npm install to build dependencies
RUN npm install

# Add the current directories contents to the container's /src directory
ADD . . 

# Define the default run command. Not actually ran on [docker build]. 
# This command is only executed on [docker run].
CMD ["npm", "start"]  

The Docker Image

A Docker image is an immutable starting point for your containers. Think Ubuntu with NodeJs preinstalled or a Redis instance.

Build your docker image

  1. Run docker build -t my-docker-image:0.1 . Note on first run this will take 1-5 minutes to complete.
    • docker is the cli in which you will use to manipulate docker images and containers
    • build is the command for building your docker image
    • -t my-docker-image:0.1 is the tag (aka name:version) for your docker image
    • . is the directory in which your Dockerfile is located. In our case it is the cwd.
  2. Run docker images
    • images is the command to list out the images that have been created on this docker machine.
    • You should see two images:
      • node because it is listed in your Dockerfile as the base image
      • my-docker-image because that is the name you provided for you custom image

The Docker Container

A Docker Container is just an application sandbox.

Create an environment file (optional)

  1. Run touch app.env
    • The easiest way to set environment variables in a container.
    • Values are name=value formatted and line delimited E.g. NODE_ENV=dev
    • This file is not required or named with any sort of convention. It is just an easy way to set container env vars.
  2. Paste the following into your app.env file:

Create and run your Docker container

  1. Run docker run -d -p 80:3000 --expose 3000 --env-file app.env --name my-docker-container my-docker-image:0.1
    • run is the command to create and run your docker build. The result of this command is a docker container.
    • -d is the switch to leave your container running in the background (detached mode)
    • -p 80:3000 is used to define port forwarding in the format of external:internal. E.g. traffic going to port 80 is internally redirected to the app running on port 3000 inside the container.
    • --expose 3000 is used to define the port that the container exposes
    • --env-file app.env is used to define which environment variables need set inside the container. Optional.
    • --name my-docker-container is the custom name for your new docker container. Optional.
  2. Run docker ps
    • ps is the command to list out all of the created docker containers
    • You should see your my-docker-container here now.
  3. Run open http://$(docker-machine ip my-machine)
    • ip is the command to get the given machine's ip address
    • Copy the output of this command into your browser to see the application that is exposed from your container.
    • You can add a hosts file entry for your docker machine so you don't have to keep track of it. Note it does change from time to time.

Note the following commands for future use:

Docker Compose

Docker Compose is a cli for defining and running multi-container applications with Docker. With Compose, you define a multi-container application in a single file, then spin your application up in a single command which does everything that needs to be done to get it running.

Creating your docker-compose.yml file

  1. Run touch docker-compose.yml
  2. Paste the following into the docker-compose.yml file:
  build: . # Use the current directory as the place to find the appropriate Dockerfile
    - "app.env" # Load any environment variables we want to set in the container
    - "80:3000" # Port forwarding external:internal
    - "3000" # Open the container port so it can be accessed
  1. This should look extremely familiar to the docker run ... command we ran earlier to create our first container.
  2. Run docker-compose up -d web
    • up is the command used to read in the docker-compose.yml file and spin up the appropriate containers.
    • -d is the switch used to run this command in the background (detached mode)
    • web is the name of the specific service in the docker-compose.yml file. Providing no name will run all the service defined in the docker-compose.yml file.

Note the following commands for future use:

Bonus points

Adding hosts file entry

From time to time when you start up your docker machine its IP address will change. That got annoying real quick so I found the hostile npm package. Once installed globally, updating the host entry was a breeze.

  1. Run npm install -g hostile
  2. Run docker-machine status my-machine to make sure your docker machine is up and running.
  3. If not start it up and run eval $(docker-machine env my-machine)
  4. Run sudo hostile set "$(docker-machine ip my-machine)" "mysubdomain.mytld.com"
  5. It is a good idea to run the hostile command above everytime you start up your docker machine.
  6. That's it, now when you docker-compose up -d web you should be able to visit the running app at http://mysubdomain.mytld.com
Nate Clark

Nate Clark