Skip to main content

Dockerfile structure

Understanding the Structure of a Dockerfile

A Dockerfile is a set of instructions that define how to build a Docker image. It acts as a blueprint for creating containerized applications, specifying everything from the base image to commands, environment variables, and metadata.

By defining these instructions, Dockerfiles enable consistent and repeatable builds, making them an essential part of modern containerized development. By understanding its sections and how to use them effectively, you can create robust, reusable, and optimized Docker images. This article explores all the key sections available in a Dockerfile, their purpose, and usage.


Videotutorial

See also...

Watch this tutorial on Dockerfile Structure.


Key Sections of a Dockerfile

1. FROM

Specifies the base image to use for creating the container. This is typically the first instruction in a Dockerfile.

  • Example:
    FROM ubuntu:20.04

2. LABEL

Provides metadata for the image, such as author information, version, or description. This metadata is useful for documentation and automation.

  • Example:
    LABEL maintainer="developer@example.com" \
    version="1.0" \
    description="A sample Dockerfile for demonstration."

3. RUN

Executes commands in the image during the build process, such as installing software or setting up configurations. Each RUN instruction creates a new image layer. This layering is beneficial because it allows for caching, meaning subsequent builds can reuse layers that haven't changed, speeding up the build process. However, creating excessive layers or redundant instructions can increase image size and make the build process less efficient. To optimize, it's recommended to combine commands where appropriate.

  • Example:
    RUN apt-get update && apt-get install -y curl

4. WORKDIR

Sets the working directory inside the container. All subsequent instructions, such as RUN, CMD, or COPY, are executed relative to this directory.

  • Example:
    WORKDIR /app

5. COPY and ADD

Transfers files from the host machine to the image:

  • COPY: Basic copying of files or directories.

  • ADD: Similar to COPY but supports URL downloads and file extraction (e.g., tar files).

  • Example:

    COPY . /app
    ADD https://example.com/file.tar.gz /app

6. ENV

Sets environment variables inside the container, which can be accessed by applications running in the container.

  • Example:
    ENV APP_ENV=production

7. EXPOSE

Documents the port(s) the container will listen on at runtime. Note that this does not actually publish the ports; you need to use -p or --publish when running the container.

  • Example:
    EXPOSE 8080

8. CMD

Specifies the default command to run when a container is started from the image. If both CMD and ENTRYPOINT are present, CMD provides the default arguments for the ENTRYPOINT command. This allows greater flexibility, as CMD can act as a fallback or complement to ENTRYPOINT. Only one CMD instruction is allowed per Dockerfile.

  • Example:
    CMD ["node", "app.js"]

9. ENTRYPOINT

Defines a command that always runs when the container starts. Unlike CMD, ENTRYPOINT allows you to pass additional arguments during runtime.

  • Example:
    ENTRYPOINT ["/bin/bash", "-c"]

10. VOLUME

Creates a mount point for external storage, enabling data persistence outside the container.

  • Example:
    VOLUME ["/data"]

11. ARG

Defines build-time variables that can be passed when building the image with docker build.

  • Example:
    ARG VERSION=1.0

12. ONBUILD

Defines instructions to execute when the image is used as a base image in another Dockerfile.

  • Example:
    ONBUILD RUN apt-get update

13. HEALTHCHECK

Specifies a command to test if the container is running properly. It can help monitor the health of the application.

  • Example:
    HEALTHCHECK --interval=30s --timeout=5s CMD curl -f http://localhost/ || exit 1

Practice: Build and Run a Complex Docker Image

In this exercise, you'll create a Dockerfile that sets up a Node.js application and demonstrates the use of multiple Dockerfile sections.

  1. Create a Project Directory

    mkdir 02_01_docker-practice
    cd 02_01_docker-practice
  2. Create the Application Inside the project directory, create a simple Node.js application:

    • app.js:

      const http = require('http');

      const server = http.createServer((req, res) => {
      res.writeHead(200, { 'Content-Type': 'text/plain' });
      res.end('Hello, Dockerfile World!\n');
      });

      server.listen(8080, () => {
      console.log('Server running on http://localhost:8080');
      });
    • package.json:

      {
      "name": "docker-practice",
      "version": "1.0.0",
      "main": "app.js",
      "dependencies": {
      "http": "^0.0.1-security"
      }
      }
  3. Create the Dockerfile

    • Dockerfile:
      # Step 1: Specify the base image
      FROM node:14

      # Step 2: Add metadata
      LABEL maintainer="developer@example.com" \
      version="1.0" \
      description="Node.js application example."

      # Step 3: Set working directory
      WORKDIR /app

      # Step 4: Copy application files
      COPY package*.json ./
      COPY app.js ./

      # Step 5: Install dependencies
      RUN npm install

      # Step 6: Expose application port
      EXPOSE 8080

      # Step 7: Define default command
      CMD ["node", "app.js"]
  4. Build the Docker Image Run the following command to build the image:

    docker build -t node-app:1.0 .
  5. Run the Container Start a container using the built image:

    docker run -p 8080:8080 node-app:1.0
  6. Access the Application Open your browser and navigate to http://localhost:8080. You should see:

    Hello, Dockerfile World!

Summary

By understanding Dockerfile sections and practicing with real-world examples, you can build advanced containerized applications ready for deployment on edge nodes or cloud environments. Each section in a Dockerfile plays a critical role in defining the image, from selecting a base image with FROM to optimizing builds with RUN instructions, or setting runtime behavior using CMD and ENTRYPOINT. Mastering these sections ensures efficient, maintainable, and versatile container builds, tailored to a variety of deployment scenarios.