Skip to main content

The Dockerfile

Divio uses dockerised applications. Each Docker image is defined by a Dockerfile, that describes what is in the image and how containers created from it should be built.

The Dockerfile is simply a text document, containing all the commands that would be issued on the command-line to build an image - in short, the Dockerfile defines an environment.

The Dockerfile is built automatically, and populated with appropriate commands (see below). However, you can also add any commands you wish to the Dockerfile, for example to install system packages, or to configure the environment.

important

The Dockerfile executes its commands in sequence. This means that commands to install Node (for example) must come before commands to run Node packages.

Example Dockerfile

The following example is taken from our Getting Started with Next.js template:

FROM node:lts-alpine

ENV NODE_PATH=/app/node_modules

WORKDIR /app

COPY package*.json /app/
RUN npm install

COPY . /app
RUN npm run build

EXPOSE 80

CMD ["npm", "start", "--", "-p", "80"]

This Dockerfile is designed to create a Docker container that is ready to run a Node.js application, with all dependencies installed and ports configured to expose the app on port 80. Here's a step-by-step explanation of that Dockerfile:

  • FROM node:20.14
    This line specifies the base image to use for the Docker container. It uses a specific version (20.14) of the official Node.js Docker image. We recommend to specifically pin your versions.

  • ENV NODE_PATH=/app/node_modules
    Sets an environment variable within the container. NODE_PATH is used by Node.js to determine where to look for modules. Here, it is set to /app/node_modules, which is where the dependencies will be installed. This helps Node.js find installed modules more efficiently.

  • WORKDIR /app
    Sets the working directory for any RUN, CMD, ENTRYPOINT, COPY, and ADD commands that follow in the Dockerfile. If the directory does not exist, it will be created. All subsequent actions will be performed within this directory.

  • COPY package.json /app/
    Copies both package.json and package-lock.json (if present, due to the wildcard pattern package\*.json) from your project folder into the /app directory inside the container. This is added separately to cache the installation of packages.

  • RUN npm install
    Runs npm install inside your container, which installs all the dependencies defined in package.json and package-lock.json. These dependencies will be placed in the /app/node_modules directory, aligning with the NODE_PATH set earlier.

  • COPY . /app
    Copies the rest of your project's files and directories into the /app directory inside the container. This step is done after installing dependencies to ensure that changes in your source code don’t invalidate the Docker cache of the npm install step.

  • RUN npm run build
    Executes the build script specified in your package.json. This command is typically used to compile or transpile the application, prepare it for production by bundling or minifying it.

  • EXPOSE 80
    Informs Docker that the container listens on port 80 at runtime. This is a way of documenting which ports are intended to be published. It doesn’t actually publish the port; it functions as a type of documentation between the person who builds the image and the person who runs the container.

  • CMD ["npm", "start", "--", "-p", "80"]
    Specifies the command that will be executed when the Docker container starts. Here, it runs npm start -- -p 80, which typically starts your Node.js application and specifies that it should run on port 80.

Docker caching

Docker's caching mechanism is designed to speed up image builds by reusing layers from previous builds when possible. When Docker builds an image, it processes each instruction in the Dockerfile sequentially and checks if an identical instruction has been executed in a previous build. If a match is found, Docker uses the cached layer instead of generating a new one.

This caching is particularly effective up to the point where a file has changed (e.g., a COPY command that adds files from the host). After a change is detected, all subsequent layers will be rebuilt. To make the most efficient use of Docker's cache, you should structure your Dockerfile to handle frequently changing steps (like copying source code) after infrequently changing steps (like installing packages).

For more detailed information on Docker's caching strategies, you can refer to the official Docker documentation.