Docker Build Basics

10 min read Beginner Updated: May 16, 2025

Introduction to Docker Images

Docker images are the foundation of containerization. They are lightweight, standalone, executable packages that include everything needed to run an application: code, runtime, libraries, environment variables, and configuration files.

A Docker image is a read-only template that contains instructions for creating a Docker container. Images are created with a series of layered instructions that are executed in order. Each instruction creates a new layer in the image, building upon the previous layers.

Before we dive into building images, it's important to understand these key concepts:

  • Images: Read-only templates used to create containers
  • Containers: Running instances of Docker images
  • Layers: Each step in a Dockerfile creates a layer, which are cached to speed up future builds
  • Dockerfile: A text file with instructions for building a Docker image
  • Registry: A service that stores Docker images (like Docker Hub)

Understanding Dockerfiles

A Dockerfile is a text document that contains the instructions to assemble a Docker image. It specifies the base image to use, commands to execute, and files to add into the image.

Here's a simple example of a Dockerfile for a Node.js application:

Dockerfile
# Use an official Node.js runtime as the base image
FROM node:18-alpine

# Set the working directory in the container
WORKDIR /app

# Copy package.json and package-lock.json to the working directory
COPY package*.json ./

# Install the application dependencies
RUN npm install

# Copy the application code to the working directory
COPY . .

# Expose port 3000
EXPOSE 3000

# Define the command to run the application
CMD ["npm", "start"]

This Dockerfile includes several commands, each creating a new layer in the image. Let's understand what each of these commands does.

Essential Dockerfile Commands

FROM

The FROM instruction initializes a new build stage and sets the base image. All Dockerfiles must start with a FROM instruction. It's recommended to use official images from Docker Hub.

FROM node:18-alpine

WORKDIR

The WORKDIR instruction sets the working directory for any subsequent RUN, CMD, ENTRYPOINT, COPY, and ADD instructions.

WORKDIR /app

COPY

The COPY instruction copies files or directories from the source (host) to the destination (container filesystem).

COPY package*.json ./
COPY . .

RUN

The RUN instruction executes commands in a new layer on top of the current image. It's used for installing packages, compiling code, etc.

RUN npm install

EXPOSE

The EXPOSE instruction informs Docker that the container listens on the specified network ports at runtime. It does not actually publish the port; you need to use the -p flag with docker run for that.

EXPOSE 3000

CMD

The CMD instruction provides defaults for executing a container. There can only be one CMD instruction in a Dockerfile.

CMD ["npm", "start"]

Other Important Commands

  • ENV: Sets environment variables
  • ARG: Defines build-time variables
  • ENTRYPOINT: Configures a container to run as an executable
  • VOLUME: Creates a mount point for external volumes
  • USER: Sets the user name or UID to use when running the image

Best Practices

Following best practices when creating Dockerfiles ensures your images are efficient, secure, and easy to maintain:

1. Use Official and Specific Base Images

Always use official images from trusted sources. Specify the exact version to avoid unexpected changes.

# Good
FROM node:18.16.0-alpine3.17

# Avoid
FROM node:latest

2. Optimize Layer Caching

Order your Dockerfile instructions from least to most frequently changing to take advantage of Docker's caching mechanism. For example, copy your package.json files before copying your source code.

3. Minimize the Number of Layers

Combine related commands in a single RUN instruction using the shell's && operator and \ line continuation.

# Good
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    package1 \
    package2 && \
    rm -rf /var/lib/apt/lists/*

# Avoid
RUN apt-get update
RUN apt-get install -y package1
RUN apt-get install -y package2
RUN rm -rf /var/lib/apt/lists/*

4. Remove Unnecessary Files

Don't include files that aren't needed in your Docker image. Use .dockerignore to exclude files and directories.

5. Use Multi-stage Builds for Smaller Images

Use multi-stage builds to create leaner production images, especially for compiled languages.

6. Use Non-root Users

Avoid running containers as root for security best practices.

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

The Docker Build Command

Once you've created your Dockerfile, you can build an image using the docker build command. Here's the basic syntax:

docker build [OPTIONS] PATH | URL | -

Common options include:

  • -t, --tag: Name and optionally a tag in the 'name:tag' format
  • --no-cache: Do not use cache when building the image
  • --pull: Always attempt to pull a newer version of the base image
  • -f, --file: Path to the Dockerfile (default: 'PATH/Dockerfile')

Example:

docker build -t myapp:1.0 .

This command builds an image from the Dockerfile in the current directory and tags it as "myapp:1.0".

Basic Image Optimization

Optimizing your Docker images is crucial for improving build times, reducing image size, and enhancing security.

1. Use Smaller Base Images

Alpine-based images are significantly smaller than their Debian/Ubuntu counterparts.

# ~140MB
FROM node:18-alpine

# ~900MB
FROM node:18

2. Clean Up in the Same Layer

When installing packages, clean up in the same RUN instruction to avoid creating extra layers with unnecessary files.

RUN apt-get update && \
    apt-get install -y package1 package2 && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

3. Use .dockerignore

Create a .dockerignore file to exclude files and directories that aren't needed in the image, similar to .gitignore.

# Example .dockerignore
node_modules
npm-debug.log
.git
.dockerignore
Dockerfile
.env
*.md
tests/

4. Minimize Installed Dependencies

Only install what's necessary for your application to run. For example, use --no-install-recommends with apt-get.

Conclusion

In this tutorial, we've covered the basics of Docker image building, including understanding Dockerfiles, essential commands, best practices, and basic optimization techniques.

Building efficient Docker images is a skill that develops with practice. Start simple and gradually incorporate more advanced techniques as you become comfortable with the basics.

Ready to Take Your Docker Skills Further?

Check out our tutorial on Multi-stage Builds to learn how to create even smaller and more efficient Docker images.

Next Tutorial: Multi-stage Builds

Need Help?

Join our community forum to ask questions and get answers from Docker experts.

Join Community

Dockerfile Optimizer

Analyze your Dockerfile for best practices and performance improvements.

Try It Now