Building Custom Docker Images
This guide demonstrates how to create a Dockerfile to customize a Docker image. The process focuses on the build workflow rather than detailed instruction explanations.
Example: Creating a Custom Nginx Image
To create a custom Nginx image with a customized index.html file, follow these steps:
- Create an empty directory
- Create a file named Dockerfile with the following content:
FROM nginx
RUN echo 'Custom Nginx image built locally' > /usr/share/nginx/html/index.html
Understanding FROM and RUN Instructions
FROM: Specifies the base image for the custom image. All subsequent operations are performed on this base image. In our example, nginx serves as the foundation.
RUN: Executes commands in the shell during the image build process. Two formats are available:
- Shell format: RUN <command>
- Exec format: RUN ["executable", "param1", "param2"]
Note: Each Dockerfile instruction creates a new image layer. Excessive layers can lead to bloated images. To minimize layers, combine commands using the && operator:
# Inefficient - creates multiple layers
FROM centos
RUN yum -y install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz
# Efficient - creates single layer
FROM centos
RUN yum -y install wget \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& tar -xvf redis.tar.gz
Building the Image
To build the image from the Dockerfile, navigate to the directory containing the Dockerfile and execute:
$ docker build -t custom-nginx:v1 .
The dot at the end specifies the build context path, which we'll explore next.
Understanding Build Context
The build context path (the final dot in the docker build command) indicates which files Docker should include in the build process. Since Docker operates in a client-server model, the build happens on the Docker daemon, not locally. The context allows you to provide local files to the daemon.
If no context path is specified, Docker defaults to the directory containing the Dockerfile.
Important: Only include necessary files in the build context to avoid slow build times.
Dockerfile Instructions Reference
| Instruction | Description |
|---|---|
| FROM | Specifies the base image for subsequent instructions |
| MAINTAINER | Specifies the author/maintainer of the Dockerfile (deprecated, use LABEL instead) |
| LABEL | Adds metadata to the image using key-value pairs |
| RUN | Executes commands in the image during build time |
| CMD | Specifies the default command for container startup (can be overridden) |
| ENTRYPOINT | Sets the main command for container startup (not easily overridden) |
| EXPOSE | Declports the container will listen on at runtime |
| ENV | Sets environment variables inside the container |
| ADD | Copies files, directories, or remote URLs to the image |
| COPY | Copies files or directories to the image |
| VOLUME | Creates mount points or declares volumes |
| WORKDIR | Sets the working directory for subsequent instructions |
| USER | Specifies the user context for subsequent instructions |
| ARG | Defines variables passed to the builder during build time |
| ONBUILD | Adds trigger instructions when the image is used as a base for another build |
| STOPSIGNAL | Sets the system call signal to send to the container to exit |
| HEALTHCHECK | Defines commands to check container health periodically |
| SHELL | Overrides the default shell for RUN, CMD, and ENTRYPOINT |
Instruction Details
COPY Instruction
The COPY instruction copies files or directories from the build context to the specified path in the container:
COPY [--chown=<user>:<group>] <source>... <destination>
COPY [--chown=<user>:<group>] ["<source>",... "<destination>"]
The --chown parameter changes ownership of copied files. Source paths can use wildcards following Go's filepath.Match rules.
ADD Instruction
ADD is similar to COPY but with additional features:
- Automatically extracts compressed files (gzip, bzip2, xz)
- Can fetch files from URLs
However, ADD may invalidate build caches when copying tar files without extraction, potentially slowing builds.
CMD Instruction
CMD specifies the default command to run when a container starts. It can be overridden via command-line arguments. Only the last CMD instruction in a Dockerfile takes effect.
CMD <shell command>
CMD ["<executable>", "<param1>", "<param2>",...]
CMD ["<param1>", "<param2>",...] # Used as default parameters for ENTRYPOINT
ENTRYPOINT Instruction
ENTRYPOINT sets the main command that runs when the container starts. Unlike CMD, its executable is not easily overridden (requires --entrypoint flag). Only the last ENTRYPOINT instruction takes effect.
ENTRYPOINT can be combined with CMD to provide default parameters:
FROM nginx
ENTRYPOINT ["nginx", "-c"] # Fixed parameters
CMD ["/etc/nginx/nginx.conf"] # Variable parameters
ENV Instruction
ENV sets environment variables that can be referenced in subsequent instructions:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
ARG Instruction
ARG defines build-time variables that only exist during the build process. They can be overridden using the --build-arg flag:
ARG <name>[=<default value>]
VOLUME Instruction
VOLUME creates anonymous data volumes to persist data outside the container filesystem:
VOLUME ["<path1>", "<path2>"...]
VOLUME <path>
EXPOSE Instruction
EXPOSE declares ports that the container listens on, helping users understand which ports to map:
EXPOSE <port> [<port>...]
WORKDIR Instruction
WORKDIR sets the working directory for subsequent instructions. If the directory doesn't exist, it will be created:
WORKDIR <path>
USER Instruction
USER specifies the user and group to run subsequent commands. The user must already exist in the image:
USER <username>[:<group>]
HEALTHCHECK Instruction
HEALTHCHECK defines commands to monitor container health:
HEALTHCHECK [OPTIONS] CMD <command>
HEALTHCHECK NONE
ONBUILD Instruction
ONBUILD defines trigger instructions that execute when the image is used as a base for another build:
ONBUILD <instruction>
LABEL Instruction
LABEL adds metadata to the image in key-value format:
LABEL <key>=<value> <key>=<value>...