Docker Build: How to Build a Docker Image from Dockerfile?
Docker build is a CLI command that creates container images from a set of instructions defined in a Dockerfile. It reads each instruction, executes it in order, and produces a layered, portable image you can run anywhere Docker is supported.
The command has been a core part of the Docker toolkit since Docker's early days. Since Docker Engine 23.0, BuildKit serves as the default build backend, bringing parallel execution, better caching, and build secrets support out of the box. When you run docker build today, you are using BuildKit under the hood.
This article covers the Docker build command's syntax, commonly used flags, and the step-by-step process for building an image from a Dockerfile. You will also learn how to optimize builds with .dockerignore files, multi-stage builds, and layer caching strategies.
#Prerequisites
Before you start this tutorial make sure that:
- You have the latest version of Ubuntu installed on your system.
- Docker is installed on your host machine.
- Your account has admin rights.
#What does Docker build do?
The docker build command creates Docker images from a set of instructions written in a Dockerfile. A Dockerfile defines your container's base image, dependencies, configuration, and startup behavior. The docker build command reads these instructions, executes them in sequence, and produces a reusable container image.
Docker build is a command-line interface (CLI) tool. It automates image creation, making builds consistent and repeatable across development, staging, and production environments.
Since Docker Engine 23.0, docker build uses BuildKit as its default backend. BuildKit processes build steps in parallel where possible, tracks file changes more efficiently, and supports advanced features like build secrets and SSH forwarding.
For extended capabilities such as multi-platform builds, you can use docker buildx build, which exposes the full BuildKit feature set.
#Syntax and usage of the Docker build command
To be able to use the Docker build command, you must first understand its syntax. Understanding the syntax and available options will help you customize the build process according to your requirements. The docker build command is run with the following syntax:
docker build [OPTIONS] PATH | URL
OPTIONS: This refers to the flags and options that modify the build behavior.PATH | URL: This represents the build context or where you want to set up your docker container, which can be a local directory or a remote Git repository.
Ready to supercharge your Docker infrastructure? Scale effortlessly and enjoy flexible storage with Cherry Servers bare metal or virtual servers. Eliminate infrastructure headaches with free 24/7 technical support, pay-as-you-go pricing, and global availability.
#Docker build command: commonly used options and flags with examples
The following flags give you control over tagging, Dockerfile selection, build-time variables, caching, and output. Here are the most commonly used options:
-
-tor--tag: This option allows you to assign a tag to the built image for easy referencing and versioning. In this example, the built image will be tagged as "sampleapp" with the "latest" version.Example usage:
docker build -t sampleapp:latest . -
-for-file: This option can be used to specify a different Dockerfile name or location if it is not named "Dockerfile." In this example, the image will be built using the Dockerfile named "ProductionDockerfile" in the current directory.Example usage:
docker build -fProductionDockerfile. -
--build-arg: The--build-argoption allows you to pass build-time variables to the Dockerfile, enabling dynamic customization during the build process. This command passes a build-time variable named "VERSION" with the value "10" to the Dockerfile.Example usage:
docker build --build-arg VERSION=10. -
--no-cache: This option can be used to force Docker to ignore the cached layers and perform a fresh build. This command forces Docker to ignore the cache and perform a fresh build using all layers.Example usage:
docker build --no-cache . -
--target: If your Dockerfile has multiple build stages defined using theFROMinstruction, the--targetoption allows you to specify a specific target stage to build. In this example, only the stage defined as "mytarget" in the Dockerfile will be built.Example usage:
docker build --target mytarget . -
--quietor-q: The--quietoption suppresses the build output, displaying only the final image ID after the image has been built successfully. This command performs the build silently, showing only the resulting image ID.Example usage:
docker build --quiet .
-
--platform: The--platformflag sets the target platform for the build. Use it when building images for a different architecture than your host machine. For example, building an ARM image on an x86 machine.Example usage:
docker build --platform linux/arm64 . -
--secret: The--secretflag passes sensitive data (like API keys or tokens) to the build without embedding them in the final image. The secret is only available during the build step that references it.Example usage:
docker build --secret id=mytoken,src=token.txt .
These are some of the most commonly used options. You can explore the full list in the official Docker build reference.
#How does Docker build work?
The Docker build process creates a Docker image based on instructions in a Dockerfile. This file contains build instructions like copying files, installing dependencies, running commands, and more.
When you run docker build, the Docker daemon reads the Dockerfile and executes each instruction in order, generating a layered image. Each instruction creates a lightweight, read-only filesystem snapshot known as a layer. Docker caches unchanged layers to speed up subsequent builds. Layers that haven't changed since a previous build are cached and reused, avoiding the need to rebuild those layers and significantly improving build times.
Once all instructions are executed, Docker creates the final image containing the application and its dependencies, ready to be used to run containers.
#How to build a Docker image from Dockerfile: Step-by-step
The steps below walk through building a simple Docker image: an Nginx web server that serves a static HTML page. You will create a Dockerfile, build the image, run it as a container, and access the application in your browser.
#Step 1 - Create a working directory
Create a directory or folder to use for this demonstration ("docker-demo" is used here as the directory) and navigate to that directory by running the following commands in your terminal:
mkdir docker-demo
cd docker-demo
Create a file called "index.html" in the directory and add the following content to it:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Simple App</title>
</head>
<body>
<h1>Hello World</h1>
<p>This is running in a docker container</p>
</body>
</html>
This will serve as the web page to be served up by the server.
#Step 2 - Select a base image
Next, select a suitable base image from Docker Hub or a local repository. The base image forms the foundation of your custom image and contains the operating system and essential dependencies. Almost every single image for Docker is based on another image. For this demonstration, you will use nginx:stable-alpine-slim as the base image. The stable-alpine-slim tag pulls the latest stable Nginx release on a minimal Alpine Linux base, keeping the image size small.
#Step 3 - Create a Dockerfile
Now create a file named "Dockerfile". This file will define the build instructions for your image. By default, when you run the docker build command, docker searches for the Dockerfile.
#Step 4 - Add build instructions to the Dockerfile
Open the Dockerfile in a text editor and add the following lines:
FROM nginx:stable-alpine3.17-slim
COPY index.html /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
The instruction above will create a Docker image that serves the provided "index.html" file through the Nginx web server when a container is launched based on that image.
- The FROM instruction initializes a new build stage and sets the base image for subsequent instructions.
- The COPY instruction copies files or directories (usually the source code) from the source to the specified location inside the image. It copies the file to the "/usr/share/nginx/html" directory, which is the default location for serving static files in the Nginx web server.
- The main purpose of the CMD instruction is to provide defaults for executing containers.
The instructions defined in Dockerfiles differ based on the kind of image you're trying to build.
#Step 5 - Build the image using the Docker build command
Before building the Docker image, confirm that you have Docker installed by running the docker --version command.
docker --version
OutputDocker version 29.2.1, build a5c7197
To build your container, make sure you're in the folder where you have your Dockerfile and run the following command in the terminal:
docker build -t sampleapp:v1 .
This command initiates the Docker build process to create a Docker image based on the instructions specified in the Dockerfile located in the current directory (.)
The -t flag specifies a tag for the image, allowing you to assign a name and version to it. In this case, the image will be tagged as "sampleapp" with the version "v1" providing a descriptive identifier for the image, making it easier to reference and manage.
You should see the build process start and an output indicating that it has finished when it is done.
Also read: How to run Docker on bare metal cloud
#Step 6 - Verify the built Docker image
After a successful build, verify the image by running the docker images command to list all the available images on your Docker host. You should see your newly created image listed with its assigned tag and other relevant details, ready to be used for running containers or pushing to a container registry for distribution.
docker images
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
sampleapp:v1 d484b7e19e06 12.6MB 0B
#Step 7 - Run the Docker image
Next, run the Docker image as a container using:
docker run -p 8080:80 sampleapp:v1
Output/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2026/02/24 04:57:31 [notice] 1#1: using the "epoll" event method
2026/02/24 04:57:31 [notice] 1#1: nginx/1.28.2
2026/02/24 04:57:31 [notice] 1#1: built by gcc 15.2.0 (Alpine 15.2.0)
2026/02/24 04:57:31 [notice] 1#1: OS: Linux 6.8.0-101-generic
2026/02/24 04:57:31 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1024:524288
2026/02/24 04:57:31 [notice] 1#1: start worker processes
2026/02/24 04:57:31 [notice] 1#1: start worker process 30
This command tells Docker to run the sampleapp container. The -p flag specifies the port mapping, which maps a port from the host machine to a port inside the container. Here, you are mapping port 8080 of the host machine to port 80 of the container. You can modify the host port as per your preference. Ensure you specify the image name and version you used when building the image.
Running the container without the -d flag blocks your terminal. No other commands can run while the container is active. Alternatively, you can add the -d (detach) flag to run the container in the background and keep your terminal free:
docker run -d -p 8080:80 sampleapp:v1
3f92a1c4b7e2d8f05c6a9b3e1d7f4a02c8e5b9d1f3a6c2e8b0d4f7a1c5e9b2d
Docker prints the full container ID and returns control of your terminal immediately. The container continues running in the background.
#Step 8 - Access the application
With the container running, you can go ahead to access the application. Open a web browser and navigate to localhost:8080 and you should see the sample web page displayed on your web browser.
#How to optimize and quicken the Docker build process using a .dockerignore file
The process of building an image for the first time using docker build is time-consuming. Not every file in your application code package is crucial. Some files are needed while others have to be ignored by the Docker daemon during the build process to save time.
The main problem is that, when Docker builds an image, it sends everything in the build context (by default, the current directory) to the Docker daemon. Fortunately, you can control which files get sent by using a .dockerignore file. This file tells Docker not to include the specified directories and files when sending the build context to the Docker daemon.
Below are some of the files you should add to the .dockerignore file:
- Version control directories.
- Dependency directories if you’re installing dependencies during the build.
- Local configuration files and other environment-specific settings.
- Build artifacts or compiled files that aren’t needed in the image.
Below is an example of a .dockerignore file:
# Exclude version control directories
.git
.svn
# Exclude Node.js dependencies (assuming you install them during build)
node_modules
# Exclude Python cache and compiled files
__pycache__
*.pyc
*.pyo
# Exclude build and distribution directories
build
dist
# Exclude log files and temporary directories
*.log
tmp/
# Exclude OS-specific files
.DS_Store
Thumbs.db
You can adjust it based on the specifics of your project. This helps reduce the build context size, speeds up the build process, and avoids including potentially sensitive or unnecessary files in your image.
#How to reduce image size with multi-stage builds
Multi-stage builds let you use multiple FROM statements in a single Dockerfile. Each FROM instruction starts a new build stage. You can copy artifacts from one stage to another and leave behind everything the final image does not need, such as build tools, compilers, intermediate files, and development dependencies.
The result is a smaller, cleaner production image. Without multi-stage builds, you would either ship an image bloated with build tools or maintain separate Dockerfiles for building and running your application.
Here is a practical example using a Node.js application:
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Production
FROM nginx:stable-alpine-slim
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
The first stage (builder) pulls a full Node.js image, installs dependencies, and compiles the application. The second stage starts fresh from a minimal Nginx image and copies only the compiled output from the builder stage. The final image contains just Nginx and the built files. No Node.js runtime, node_modules, or source code.
Build the image the same way as any other Dockerfile:
docker build -t myapp:prod .
You can also build a specific stage using the --target flag. Running docker build --target builder . stops the build after the first stage completes. Teams often use this approach to run tests in a CI pipeline without producing the final production image.
For a deeper walkthrough, see our guide on Docker multi-stage builds.
#Best practices for optimizing Docker builds
Here are some best practices you can follow to optimize your Docker build and ensure efficiency:
- Ensure you keep your Dockerfiles concise, organized, and easy to maintain. Break down complex tasks into smaller, reusable instructions, thereby improving readability, and simplifying troubleshooting and updates.
- Use lightweight images as base images in your Dockerfile and ensure you only install the essential things in the image. Smaller images not only improve performance but also reduce resource consumption during deployment.
- Use .dockerignore to ensure you're only adding the files you need.
- Take advantage of Docker's caching mechanism to speed up subsequent builds. Utilize layers that don't change frequently or use the
--no-cache=trueflag when necessary. This can be especially useful for large projects. - Consider using multi-stage builds when applicable. They allow you to separate build dependencies from the final runtime image, resulting in smaller image sizes.
- Order Dockerfile instructions from least to most frequently changed. Place
COPY package*.jsonandRUN npm installbeforeCOPY . .so that dependency installation layers stay cached when only application code changes. - Combine related
RUNcommands with&&to reduce the total number of layers. EachRUNinstruction creates a new layer, so merging commands likeapt-get update && apt-get install -y curlkeeps the image leaner.
These practices speed up build times, shrink image sizes, and produce more reliable builds. The Docker build best practices documentation covers additional strategies in detail.
#Troubleshooting Docker build command issues
Even with the best practices, you might encounter occasional hiccups during the Docker build process. Here are some common issues and their potential solutions:
- Error: "Unable to find the Dockerfile": Ensure that you are running the docker build command from the directory containing the Dockerfile.
- Build failures or unexpected results: Check the Dockerfile syntax and ensure all dependencies and commands are correctly specified. Review the build logs and error messages and consider using the --no-cache option to perform a clean build.
- "COPY failed: file not found in build context": The file you are trying to copy does not exist in the build context directory. Verify the file path is correct relative to the directory where you run
docker build. Also, check your.dockerignorefile. It might be excluding the file you need. - "permission denied" during build: Commands like
apt-get installor writing to certain directories may fail if the Dockerfile switches to a non-root user too early. PlaceUSERinstructions after all commands that need root access. If the issue happens when running the built container, check the file ownership set during the build. - Build context is too large / build is slow to start: Docker sends the entire build context to the daemon before executing any instructions. A large context (hundreds of megabytes or more) slows down every build. Add a
.dockerignorefile to exclude unnecessary directories like.git,node_modules, and build artifacts. - "--build-arg" variable not available in RUN: Build arguments are scoped to the build stage where they are declared. If you use multi-stage builds, you must redeclare the
ARGinstruction in each stage that needs the variable. Place theARGinstruction after theFROMline of the relevant stage. - Cached layers causing stale dependencies: Docker reuses cached layers when the instruction and its inputs have not changed. If you update a
requirements.txtorpackage.jsonbut theCOPYinstruction hash remains the same (rare but possible with certain file metadata), the cachedRUNlayer may serve outdated packages. Rundocker build --no-cache .to force a clean build when you suspect stale dependencies.
Also read: How Docker stores images
#Conclusion
The docker build command turns a Dockerfile into a portable container image. It reads instructions sequentially, creates cached layers for each step, and produces a final image ready for deployment.
In this guide, you learned the syntax of the build command and its commonly used flags, how the layered build process works, and how to build an Nginx image step by step. You also explored .dockerignore files, multi-stage builds, layer caching strategies, and solutions for common build errors.
To run your Docker workloads on dedicated hardware, explore Cherry Servers' bare metal and virtual server options. You may also want to read our related guides on Docker Compose commands, pulling Docker images, and Docker vs Kubernetes.
FAQs
What is the difference between `docker build` and `docker buildx build`?
`docker build` uses BuildKit as the default backend since Docker Engine 23.0. `docker buildx build` exposes the full BuildKit feature set, including multi-platform builds, custom build drivers, and remote builder instances. For single-platform builds, both commands produce identical results.
Can I build a Docker image without a Dockerfile?
Yes. You can pipe a Dockerfile through stdin using `docker build - < Dockerfile` or use `docker commit` to create an image from a running container's filesystem. However, Dockerfiles remain the recommended approach because they make builds reproducible and version-controllable.
How do I reduce the size of a Docker image?
Use a minimal base image, such as `alpine` or `*-slim` variants. Apply multi-stage builds to separate build tools from the final runtime image. Remove package manager caches (`apt-get clean`, `rm -rf /var/lib/apt/lists/*`) in the same `RUN` layer that installs packages. Add a `.dockerignore` file to exclude unnecessary files from the build context.
Why is my Docker build slow?
Common causes include a large build context (add a `.dockerignore`), poor layer ordering that invalidates the cache on every build, and downloading dependencies without caching. Order your Dockerfile instructions from least to most frequently changed so Docker can reuse cached layers more effectively.
Does `docker build` work on all operating systems?
Docker build works on Linux, macOS, and Windows. On macOS and Windows, Docker Desktop runs a Linux VM to handle container operations. The build process and Dockerfile syntax remain the same across platforms.
How do I build an image for a different architecture?
Use the `--platform` flag with `docker buildx build`. For example, `docker buildx build --platform linux/arm64 -t myapp:arm64 .` builds an ARM64 image on an x86 host. Multi-platform builds require QEMU emulation or a remote builder running the target architecture.
Starting at just $3.24 / month, get virtual servers with top-tier performance.
