skip to content
clipboard

Writing Dockerfile

The Dockerfile specifies what to be included in your application container when it is executed. Dockerfile define environment and avoid discrepancies with dependencies or runtime versions.

Create Dockerfile on your project’s root directory

nano Dockerfile

Docker images are created using a succession layered images that build on one another. The first step is to add the base image for the application that will form the starting point of the application.

Find your suitable image here. Let’s use node:12-alpine as it is the latest LTS version of NodeJS.

FROM node:12-alpine

Each Dockerfile must begin with FROM instruction.

By default the image include Node.js and npm and also non-root user that should be used to running your application. It is recommended security practice to avoid running container as root.

We will use node user’s home directory as the working directory and set them as user inside the container.

Let’s create node_modules directory under app directory in our working directory and set the ownership of the directory

FROM node:12-alpine

RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app

Let’s set the working directory

FROM node:12-alpine

RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app

WORKDIR /home/node/app

Then copy the package.json and package-lock.json

FROM node:12-alpine

RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app

WORKDIR /home/node/app

COPY package*.json ./

Adding this COPY instruction before running npm install or copying the application code allows us to take advantage of Docker’s caching mechanism. At each stage in the build, Docker will check to see if it has a layer cached for that particular instruction. If we change package.json, this layer will be rebuilt, but if we don’t, this instruction will allow Docker to use the existing image layer and skip reinstalling our node modules.

Let’s switch to non-root user / node user.

FROM node:12-alpine

RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app

WORKDIR /home/node/app

COPY package*.json ./

USER node

then run npm install

FROM node:12-alpine

RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app

WORKDIR /home/node/app

COPY package*.json ./

USER node

RUN npm install

then copy the application code with the appropriate permission to application directory

FROM node:12-alpine

RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app

WORKDIR /home/node/app

COPY package*.json ./

USER node

RUN npm install

COPY --chown=node:node . .

Let’s expose the port of 3000 on the container and start the application

FROM node:12-alpine

RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app

WORKDIR /home/node/app

COPY package*.json ./

USER node

RUN npm install

COPY --chown=node:node . .

EXPOSE 8080

CMD [ "npm", "run" ]

EXPOSE does not publish the port, but functions a way to documenting which ports on the container will be published at runtime.

Note that there should be always only one CMD instruction for each Dockerfile.

Save the dockerfile once you finished editing.