Deploy a Custom Node.js Application using Bitnami Containers

Introduction

Developers like using containers for development because they are easy to use, portable, and require less maintenance overhead compared to bare metal or virtual machines. Docker is a popular choice, because it provides tools that make it simple for developers to build, run and publish applications in containers.

If you've seen Docker in action and then wondered "can I use this with my own application", then you're in the right place.

Bitnami makes it easy to create a Docker image of your own application using its turnkey infrastructure containers. Once created, you can run and test your application with Docker, or publish it online so that others can find and use it. And, because Bitnami container images are always secure, optimized and up-to-date, you can rest assured that your application always has access to the latest language features and security fixes.

This guide walks you through the process of creating a Docker image of a custom application using a Bitnami base container, then testing it and publishing it online. It also guides you through the steps to follow when you update your application and need to rebuild and republish it for your users.

Assumptions and Prerequisites

This guide focuses on creating a Docker image from a custom Node.js application. In case you don't have a Node.js application at hand, you can use this simple "Hello world" application.

This guide makes the following assumptions:

To build and publish a Docker image of a custom application, you will typically follow these steps:

  • Step 1: Obtain the application source code
  • Step 2: Create a Dockerfile
  • Step 3: Build the Docker image
  • Step 4: Test the Docker image
  • Step 5: Publish the Docker image

Step 1: Obtain the application source code

To begin the process, ensure that you have access to the application source code. If the application code is stored in a Git repository, clone the repository, as shown below for the sample application:

$ git clone https://github.com/bitnami/tutorials
$ cd tutorials/simple-node-app

This will clone the sample repository. The source code is in the simple-node-app/app-code/ sub-directory.

Step 2: Create a Dockerfile

A Dockerfile is similar to a recipe: it contains all the ingredients needed to create a Docker image. Typically, each line represents a separate step and begins with an instruction keyword followed by a series of arguments. Learn more about the Dockerfile format.

In the app-code directory, create a file named Dockerfile and fill it with the following content:

# This Dockerfile uses the Bitnami container for Node.js, release 9.X (latest)

FROM bitnami/node:9 as builder
ENV NODE_ENV="production"

# Copy app's source code to the /app directory
COPY . /app

# The application's directory will be the working directory
WORKDIR /app

# Install Node.js dependencies defined in '/app/packages.json'
RUN npm install

FROM bitnami/node:9-prod
ENV NODE_ENV="production"
COPY --from=builder /app /app
WORKDIR /app
EXPOSE 3000

# Start the application
CMD ["npm", "start"]

This Dockerfile consists of two build stages:

  • The first stage uses the development image bitnami/node:9 to copy the application source and install the required application modules using npm install.
  • The second stage uses the production image bitnami/node:9-prod and creates a minimal Docker image that only consists of the application source, modules and runtime.

Let's take a closer look at the steps in the build stages:

  • The FROM instruction kicks off the Dockerfile and specifies the base image to use. Bitnami offers a number of container images for Docker which can be used as base images. Since the example application used in this guide is a Node.js application, Bitnami's Node.js container is the best choice for the base image.
  • The NODE_ENV environment variable is defined so that npm install only installs the application modules that are required in production environments.
  • The COPY instruction copies the source code from the current directory on the host to the /app directory in the image.
  • The RUN instruction executes a shell command. It's used to run npm install to install the application dependencies.
  • The WORKDIR instructions set the working directory for the image.
  • The CMD instruction specifies the command to run when the image starts. In this case, npm start will start the application.

Step 3: Build the Docker image

Once the Dockerfile is created, building a Docker image is as simple as calling the docker build command. Execute the command below in the directory containing the Dockerfile. Replace the DOCKER_USERNAME placeholder in the command below with your Docker account username. This Docker account username is necessary to avoid namespace errors when later pushing the image to your Docker Hub account.

$ docker build -t DOCKER_USERNAME/my-node-app:0.1.0 .
NOTE: For successful execution of this and subsequent docker commands, ensure that the user account you're using belongs to the docker group.

This will create an image named my-node-app, tagged as version 0.1.0. This tag uniquely identifies a Docker image, allowing you to deploy a specific version of the application if needed.

Here's an example of what you should see during the build process:

Image build process

Once the build process is complete, use the docker images command to verify that the image has been added to your local repository.

$ docker images | grep my-node-app

The version tag added during the docker build command also appears in the output of docker images. Here's an example of what you should see:

Image in local Docker registry

TIP: Tagging image releases is a recommended practice. Learn more about tags.

Step 4: Test the Docker image

Run your new Docker image in a container to test it with the docker run command. Replace the DOCKER_USERNAME placeholder in the command with your Docker account username.

$ docker run -it -p 3000:3000 DOCKER_USERNAME/my-node-app:0.1.0

This command runs the application in a container and makes port 3000 of the container accessible by binding it to port 3000 on the Docker host. With this, a user can access the application by browsing to port 3000 of the host.

Here is what you should see as the container starts:

Container startup

To test the application, browse to http://localhost:3000 (if the Docker host is the same machine) or http://SERVER-IP:3000 (if the Docker host is a different machine) and you should see this:

Application output

NOTE: If the Docker host is a different machine, ensure that the host firewall is configured to allow access on port 3000.

The -i and -t options to docker run allocate a terminal for the container process, while the -p option specifies the container-host port binding. Learn more about the docker run command and its options.

Step 5: Publish the Docker image

NOTE: This step requires a Docker Hub account. In case you don't already have one, sign up on the Docker website.

At this point, you have built, tagged and tested a Docker image containing your application code. To share it with others, you can upload the image to a public registry. A number of such registries are available, including Google Container Registry, Quay and others, but this guide will use Docker Hub.

To upload an image to Docker Hub, follow these steps:

  • Use the docker login command to log in:

      $ docker login
    
  • Use the docker push command to push the image to your Docker Hub account, as shown below. Replace the DOCKER_USERNAME placeholder in the tag name with your Docker account username.

      $ docker push DOCKER_USERNAME/my-node-app:0.1.0
    

    Here's what you should see:

    Image publishing process

    NOTE: Once published on Docker Hub, your image is publicly available by default.
  • Check that the image has been successfully uploaded to Docker Hub by searching for it using the docker search command, as shown below:

      $ docker search DOCKER_USERNAME/my-node-app:0.1.0
    

Once published on Docker Hub, other users can download your application and try it for themselves.

NOTE: You can also delete images from Docker Hub using the Docker website.

Handling Updates

As you continue developing your application, you will inevitably want to release fresh Docker images. This might be to include new application features or bug fixes, or to use a new version of the base container that has the latest fixes. Doing this involves regenerating the application image and republishing it to the registry.

To illustrate the process, let's perform a code change in the example application and then release a fresh Docker image with the updated code. Follow these steps:

  • Change to the app-code directory containing the application source code.
  • Edit the server.js file and substitute the string "Hello world" with "Hello Mom". Save the file.
  • Rebuild the image, tagging it as version 0.2.0. Replace the DOCKER_USERNAME placeholder in this and subsequent commands with your Docker account username.

      $ docker build -t DOCKER_USERNAME/my-node-app:0.2.0  .
    
  • Confirm that the image was successfully built and added to the local registry:

      $ docker images | grep my-node-app
    

    Revised image in local Docker registry

  • Test the new image:

      $ docker run -it -p 3000:3000 DOCKER_USERNAME/my-node-app:0.2.0
    

    Here's what you should see when you access the application through your Web browser:

    Application output

  • Publish the new image to Docker Hub:

      $ docker push DOCKER_USERNAME/my-node-app:0.2.0
    

Follow these steps every time you want to update and republish your Docker image. If you are deploying the new image to a Kubernetes cluster, read about rolling updates in our Kubernetes tutorial.

containers

Bitnami Documentation