Back to Blog
Lesson 11 of the Docker: Containers & Your First Image course
DevOpsJuly 29, 20264 min read

Anatomy of a Dockerfile: Building Custom Images from Scratch

Learn how to create a Dockerfile to automate your image builds. Master the FROM, RUN, and CMD directives to turn your code into a production-ready container.

dockerdockerfileautomationdevopscontainersbeginner
Intricate MRI brain scan displayed on a computer screen for medical analysis and diagnosis.

Previously in this course, we covered how to pull images from a registry to run pre-built software. In this lesson, we move from being consumers of images to being creators by defining our own environments using a Dockerfile.

A Dockerfile is a simple text file that acts as a blueprint for your container image. Instead of manually configuring a server, you write down the instructions, and Docker handles the build process. This transition from manual setup to automation is the cornerstone of modern DevOps.

What is a Dockerfile?

Think of a Dockerfile as a recipe. When you build an image, Docker reads this recipe line-by-line, creating a new "layer" in the image for every instruction. By using specific directives (keywords that tell Docker what to do), you ensure that your environment is exactly the same whether it runs on your laptop or a production server.

The Core Directives: FROM, RUN, and CMD

Detailed image of a running track line highlighting texture and pattern.

Every Dockerfile needs a handful of essential commands to be functional. Let’s look at the three most critical ones:

1. FROM: The Foundation

Every image must start with a base. The FROM directive tells Docker which existing image to use as your starting point. You might use ubuntu for a general Linux environment or python:3.9-slim for a pre-configured language environment.

2. RUN: The Builder

The RUN directive executes commands inside the image during the build process. This is where you install packages, create directories, or set up permissions. Every RUN command creates a new layer, so we often chain commands together to keep the image size small.

3. CMD: The Default Behavior

The CMD directive defines what command should run when a container starts from your image. While RUN happens at build-time, CMD happens at run-time. You only get one CMD per Dockerfile.

A Concrete Example

Let’s start our project by creating a simple "Hello World" web server environment. Create a file named Dockerfile (no file extension) in your project directory:

Dockerfile
# Start from a lightweight Linux distribution
FROM alpine:3.18

# Update the package index and install a simple tool
RUN apk add --no-cache curl

# Define the command to run when the container starts
CMD ["echo", "Hello, DevOps engineer!"]

In this example, we:

  1. FROM: Pulled the tiny Alpine Linux image.
  2. RUN: Installed curl so we have a networking tool available inside the container.
  3. CMD: Set the container to print a greeting whenever it launches.

Hands-on Exercise: Build Your Environment

  1. Create a folder named my-web-app.
  2. Inside that folder, create a file named Dockerfile.
  3. Paste the code above into your file.
  4. Open your terminal, navigate to the my-web-app folder, and verify the file exists with ls.

You are now ready to turn this text file into a real image in our next lesson!

Common Pitfalls

  • Case Sensitivity: Directives like FROM and RUN are traditionally written in uppercase. While Docker is case-insensitive for these keywords, the community standard is uppercase to make them stand out from your arguments.
  • The "One CMD" Rule: If you put multiple CMD lines in your Dockerfile, only the last one will execute. If you need to run multiple background services, you’ll eventually want to look at orchestration concepts, but for now, keep it simple.
  • Missing .dockerignore: Just like .gitignore, you should create a .dockerignore file to prevent copying sensitive files (like .git folders or local secrets) into your image.

FAQ

Q: Do I always have to use a base image? A: Yes. Even a "scratch" image acts as a starting point. Starting from a base image like alpine or debian provides you with a pre-configured OS, which saves you from writing hundreds of lines of configuration.

Q: How do I know which base image to choose? A: Look for "official" images on Docker Hub. They are well-maintained and audited for security.

Q: Is the build process slow? A: Initial builds take time because they download the base image, but subsequent builds are much faster thanks to the build cache. If you change a line, Docker only re-runs that line and everything after it. You can learn more about optimizing this in our guide on Docker build cache debugging.

Recap

You’ve learned that a Dockerfile is the automation backbone of your infrastructure. By using FROM, RUN, and CMD, you can define a repeatable, version-controlled environment. You've also set up the initial configuration for our project.

Up next: Building Your First Image — we will take this Dockerfile and turn it into a deployable container image.

Similar Posts