Docker From Scratch – Day 3: Understanding Docker Images and Dockerfiles

🐳 Docker From Scratch – Day 3: Understanding Docker Images and Dockerfiles

In the previous article, we installed Docker, explored Docker architecture, and ran our very first container. At this point, most beginners become comfortable running commands such as docker run nginx and docker ps. However, an important question still remains unanswered. Where does Docker actually get the Nginx application from? How does Docker know what software, libraries, and dependencies are required to run an application successfully? The answer lies in two of the most fundamental concepts in Docker: Docker Images and Dockerfiles.

To understand Docker Images, imagine that you want to build a house. Before construction begins, an architect creates a blueprint. The blueprint contains all the information needed to construct the house, including dimensions, room layouts, electrical wiring, plumbing, and structural details. The blueprint itself is not a house, but it serves as a template from which multiple houses can be built. Docker Images work in exactly the same way. A Docker Image is a read-only template that contains everything required to run an application. It includes the application code, required libraries, dependencies, runtime environment, configuration files, and operating system packages. Just as multiple houses can be built from a single blueprint, multiple containers can be created from a single Docker Image.

Before Docker Images became popular, deploying applications was often a tedious and error-prone process. A typical deployment required installing an operating system, configuring application runtimes, installing dependencies, copying application code, and manually adjusting configuration settings. Every server needed to be prepared individually. This led to one of the most common problems in software development: "It works on my machine." Developers would build applications successfully on their laptops, but operations teams would face deployment failures because the production environment differed slightly from the development environment. Docker Images solved this challenge by packaging everything together into a portable unit that could run consistently across development, testing, staging, and production environments.

One of the most powerful features of Docker Images is their layered architecture. Unlike traditional software packages, Docker Images are not stored as a single monolithic file. Instead, they are composed of multiple layers stacked on top of one another. At the bottom sits the base operating system layer, followed by runtime layers, dependency layers, and finally the application layer itself. This layered approach improves efficiency because Docker can reuse existing layers rather than rebuilding everything from scratch each time a change is made.

To better understand layers, imagine building a castle using Lego blocks. Instead of rebuilding the entire castle whenever you want to add a tower, you simply attach new blocks on top of the existing structure. Docker follows the same principle. Each instruction executed during image creation generates a new layer. These layers are cached and reused whenever possible, significantly reducing build times and storage requirements.

This brings us to Dockerfiles. A Dockerfile is a simple text file containing instructions that Docker uses to build an image. If a Docker Image is the final dish served in a restaurant, then the Dockerfile is the recipe used by the chef. The recipe describes each step required to prepare the meal, while the Dockerfile describes each step required to create the image.

Consider the following Dockerfile:

FROM nginx

COPY index.html /usr/share/nginx/html

Although it contains only two instructions, this Dockerfile is capable of building a fully functional web server. The FROM instruction specifies the base image that will be used. In this example, Docker starts with the official Nginx image. Think of the FROM instruction as the foundation of a building. Every structure needs a foundation, and every Docker Image needs a base image.

The second instruction, COPY, copies files from the local machine into the image. Here, the file index.html is copied into Nginx's default web directory. This allows us to replace the default Nginx welcome page with our own custom content. The COPY instruction is commonly used to include application source code, configuration files, scripts, and static assets inside an image.

Another important Dockerfile instruction is RUN. The RUN instruction executes commands while the image is being built. For example:

RUN apt update
RUN apt install nginx -y

Each RUN instruction creates a new image layer. These commands are executed only during image creation and become part of the final image. They are commonly used to install packages, configure software, and prepare the environment before the application is deployed.

The final instruction beginners should understand is CMD. While RUN executes commands during image creation, CMD specifies what should happen when a container starts. Think of CMD as pressing the Start button after installing an application. For example:

CMD ["nginx","-g","daemon off;"]

This instruction tells Docker to start the Nginx web server whenever a container is launched from the image.

Now let's build our first custom image. Create a simple HTML file called index.html containing:

<h1>Welcome to DevOpsVault</h1>

Next, create a Dockerfile using the Nginx image and copy the HTML file into the web server directory. Once both files are ready, execute the following command:

docker build -t devopsvault:v1 .

The docker build command instructs Docker to create an image. The -t flag assigns a name and tag to the image, while the period at the end tells Docker to use the current directory as the build context. After the build completes, the image can be viewed using:

docker images

To create a container from the image, execute:

docker run -d -p 8080:80 devopsvault:v1

The -d flag runs the container in detached mode, while -p 8080:80 maps port 8080 on the host machine to port 80 inside the container. Opening http://localhost:8080 in a browser should display the custom HTML page.

At this point, it is important to understand the complete Docker Image lifecycle. Everything begins with a Dockerfile. Docker uses the Dockerfile to build an Image. The Image then acts as a template for creating Containers. This relationship can be summarized as:

Dockerfile
      ↓
docker build
      ↓
Docker Image
      ↓
docker run
      ↓
Container

As you continue working with Docker, following best practices becomes increasingly important. Always use lightweight base images whenever possible, avoid copying unnecessary files into images, use a .dockerignore file to exclude temporary content, and avoid relying on the latest tag in production environments. Explicit versioning creates predictable and repeatable deployments.

In summary, Docker Images are the foundation of containerized applications. They provide a portable, reusable, and consistent way to package software. Dockerfiles define how those images are built, while containers bring those images to life. Understanding these concepts is essential before moving into more advanced topics such as networking, volumes, orchestration, and Kubernetes.

In the next article, we will explore Docker Networking, understand how containers communicate with each other, learn port mapping in detail, and build a real-world multi-container application.

Comments

Popular posts from this blog

Linux Commands Every DevOps Engineer Must Know (2026 Complete Guide)

Docker Day 1: Containers Explained — VM vs Docker, Lifecycle & Buildah

Kubernetes Day 7: ConfigMaps & Secrets — Managing Config the Right Way