What Is a Dockerfile?

What Is a Dockerfile?

A beginner-friendly guide to the simple text recipe that tells Docker how to build an image, explained step by step with everyday analogies and no prior knowledge needed.

Imagine you want to bake your grandmother’s famous cake, but she has never written the recipe down. Every time someone tries to make it, the result comes out a little different. One person forgets the vanilla, another uses too much sugar, and a third bakes it for the wrong amount of time. Now imagine she finally writes a clear recipe card: which ingredients to gather, in what order to mix them, and how long to bake. Suddenly anyone, anywhere, can produce the exact same cake. A Dockerfile is that recipe card, and the cake is a Docker image.

1Why Do We Need a Dockerfile?

To understand the recipe card, first imagine life without one.

Before tools like Docker became popular, setting up an application on a new computer was a long, fragile ritual. A person would install the operating system, then install a programming language, then install a dozen libraries, then copy over the application files, then change a few settings, and finally hope everything worked. These steps often lived only in someone’s memory or in a messy document that was out of date the moment it was written.

The result was a famous problem: “It works on my machine.” The application ran perfectly on the developer’s laptop but failed on the test server, because some small step had been done differently. Nobody could say exactly what the differences were, because the setup had never been written down in a precise, repeatable way.

A Dockerfile solves this by turning the whole setup ritual into a short, readable text file. Every step is written down in order. Docker reads the file and carries out each step automatically, the same way every time, on any machine. The recipe is no longer hidden in someone’s head. It lives in a file that can be read, shared, reviewed, and improved by the whole team.

Simple Analogy

A Dockerfile is like the assembly instructions that come with flat-pack furniture. Anyone who follows the numbered steps in order ends up with the same wardrobe. Without the instructions, every person would build something slightly different, and some would end up with leftover screws.

Plain text
just a readable file, no special tools needed to write it
Repeatable
the same steps give the same image every time
Shareable
a whole team can read, review, and improve it

2Some Words You Need First

A short glossary will make the rest of this guide easy to follow.

Term 1

Dockerfile

A plain text file containing the ordered steps Docker follows to build an image.

Term 2

Image

The finished, read-only package that results from a build. It holds an application and everything it needs to run.

Term 3

Container

A running instance created from an image. It is the live meal cooked from the recipe.

Term 4

Instruction

One command line inside a Dockerfile, made of a keyword followed by its details. Each tells Docker to do one job.

Term 5

Build

The act of Docker reading a Dockerfile and following its instructions to produce an image.

Term 6

Base Image

The starting point of a build, usually a small operating system or a language runtime that you build upon.

Term 7

Build Context

The folder of files that is handed to Docker during a build, from which your instructions can copy things.

Term 8

Layer

A stored set of file changes produced by a build step. An image is a stack of layers.

i
Keep This in Mind

Three words sit in a chain: a Dockerfile builds an image, and an image starts a container. The Dockerfile is the earliest link. Everything else in Docker grows out of it.

3So, What Exactly Is a Dockerfile?

A Dockerfile is a text file of instructions, read from top to bottom, that describes how to assemble an image.

A Dockerfile is an ordinary text file. By convention, it is simply named Dockerfile, with a capital D and no file extension, and it usually sits in the main folder of a project. You can open it in any text editor. There is nothing mysterious inside: just lines of text, each starting with a keyword that says what to do, followed by the details.

The file describes the whole story of an image. It says which base to start from, which extra software to add, which of your own files to bring in, which settings to apply, and which command should run when a container starts. When you ask Docker to build, it reads these lines from the first to the last, carrying out each one in order, and the final result is an image.

Declarative and Simple

The Dockerfile describes what the image should contain, not how to click through a computer to get there. You do not open windows or type into a terminal by hand. You write down the recipe once, and Docker does the work. This is why people describe Dockerfiles as a form of “infrastructure written as text,” an idea that makes environments predictable and easy to review.

Simple Analogy

Think of a Dockerfile as a shopping-and-cooking list rolled into one. It says “start with this pot, add these ingredients, stir, then serve with this sauce.” Anyone with the list and a kitchen can produce the dish without asking the original cook a single question.

!
Common Misconception

A Dockerfile is not the image, and it is not a running program. It is only the instructions. Reading a Dockerfile does not run anything by itself. The magic happens when Docker builds it into an image, and later starts a container from that image.

4Where the Dockerfile Fits in the Big Picture

The Dockerfile is the first stop on the journey from your code to a running application.

The path from an idea to a working application has several stages, and the Dockerfile sits right at the start. A developer writes the recipe. Docker builds an image from it. The image is stored, often in a registry, so that it can be shared. Then any machine can pull the image and start containers from it. Each stage produces something different, and understanding the differences prevents a lot of confusion.

flowchart LR
  A["Your project files"] --> B["Dockerfile: the recipe"]
  B -->|build| C["Image: the packaged result"]
  C -->|push| D["Registry: shared library"]
  D -->|pull| E["Any machine"]
  C -->|run| F["Container: the running application"]
  E -->|run| G["Container: another copy"]
        
FIG 1 — The Dockerfile is the recipe at the start of the chain that ends with running containers
Stage 1

Write

You write the Dockerfile as text and keep it with your project, ideally under version control.

Stage 2

Build

Docker follows the recipe and produces an image, layer by layer.

Stage 3

Share

The image is tagged with a name and version, and pushed to a registry for others to use.

Stage 4

Run

Machines pull the image and start containers from it. The Dockerfile is no longer needed at this point.

Notice the last stage. Once an image is built, the machines that run it do not need the Dockerfile at all. The image carries everything. The Dockerfile remains valuable as the source of truth, because if you ever need to rebuild or change the image, the recipe is exactly where you go.

5The Anatomy: Instructions You Will Meet

A Dockerfile is built from a small vocabulary of instructions. Learn these and you can read almost any Dockerfile.

Each line of a Dockerfile starts with an instruction keyword, conventionally written in capital letters, followed by its arguments. There are only a handful of instructions that matter for everyday work. The following cards introduce the most important ones in plain words.

Instructions That Shape the Image

Instruction

FROM

Chooses the base image to start from. It is almost always the first real instruction, and it sets the foundation of the entire stack.

Instruction

RUN

Executes a command while the image is being built, such as installing software. Its results are saved into a new layer.

Instruction

COPY

Copies files or folders from your project into the image. It is the everyday way to bring your own code in.

Instruction

ADD

Like copy, but with extras: it can fetch files from a web address and can unpack certain archives automatically.

Instruction

WORKDIR

Sets the working folder inside the image, so later steps run in the right place, like walking into the correct room before starting work.

Instruction

USER

Chooses which user account later steps and the running application will use, which matters for security.

Instructions That Record Settings

Setting

ENV

Sets an environment value that stays with the image and is visible to the running container.

Setting

ARG

Defines a value that can be supplied only while building. It disappears once the build is over.

Setting

EXPOSE

Documents which network port the application listens on. It is a helpful note, not an actual opening of the port.

Setting

VOLUME

Marks a folder as a place for data that should live outside the container’s own layers.

Setting

LABEL

Attaches descriptive information to the image, such as an author, version, or purpose.

Setting

HEALTHCHECK

Tells Docker how to test whether the application inside a container is truly working.

Instructions That Decide What Runs

Start

CMD

Sets the default command or default arguments used when a container starts. It can be overridden by whoever starts the container.

Start

ENTRYPOINT

Sets the main program that the container always runs, turning the container into something like a dedicated tool.

i
Helpful Perspective

You do not need to memorize every instruction on day one. A tiny Dockerfile can be built from just a starting image, a few installs, a copy of your code, and a start command. Learn the rest as you need them.

6How a Dockerfile Reads: Top to Bottom

The order of the lines is not decoration. It is the order of the story.

Docker reads a Dockerfile from the first line to the last, and each instruction builds on the results of the one before it. The first instruction chooses the foundation. Later instructions add to it. If you place a step too early, something it needs may not exist yet. If you place a step too late, you may lose the benefits of caching. So the sequence of lines is one of the most important design decisions in the file.

There are a few small rules of the language. A line beginning with a hash symbol is a comment, meant for human readers, and Docker ignores it. A very long instruction can be split over several lines using a continuation mark at the end of each line, which keeps the file tidy. Blank lines are ignored. Beyond that, the format is deliberately simple, and that simplicity is why beginners pick it up so quickly.

1

Choose the foundation

The first instruction selects the base image, which brings in the bottom layers of your image.

2

Prepare the environment

Next come steps that install tools, create folders, and set the working location.

3

Bring in your dependencies

The libraries your project needs are installed, ideally before your own code is copied.

4

Bring in your own code

Your application files are copied in, near the end, because they change most often.

5

Set the identity and start command

The final lines choose the user, note the port, and declare what runs when a container starts.

Simple Analogy

Building a Dockerfile is like preparing a meal in stages: gather the pots, wash and chop the vegetables, cook the base, add the delicate flavors last, and plate at the end. If you add the delicate herbs at the beginning, they are ruined. Order matters in recipes and in Dockerfiles alike.

7The Build Context and the Ignore File

A Dockerfile can only copy files that Docker has been handed. That handed-over folder is called the build context.

When you ask Docker to build an image, you point it to a folder, and that folder is packaged up and sent to the builder. This is called the build context. It is the only set of files that your copy instructions are allowed to reach. Your instructions cannot wander off and grab files from elsewhere on your computer. They can only see what is inside the context.

A common beginner mistake is to leave a huge folder in the context, such as downloaded data, test results, or a big history of version-control files. Docker dutifully sends all of it to the builder, which slows every build and can even leak files into the image that should never have been there.

The Ignore File

To solve this, Docker supports a companion file named .dockerignore, placed beside the Dockerfile. It lists the files and folders that must be left out of the build context. Think of it as a “do not pack” list. Anything named in it is never sent to the builder, so it cannot end up in the image and cannot slow down the build.

flowchart TD
  A["Project folder"] --> B{"Listed in the ignore file?"}
  B -- Yes --> C["Left out of the build context"]
  B -- No --> D["Sent to the builder as part of the context"]
  D --> E["Available to copy instructions in the Dockerfile"]
        
FIG 2 — The ignore file decides which files ever reach the builder
Simple Analogy

Packing for a trip means deciding what goes into the suitcase. The build context is the suitcase, and the ignore file is your list of things that stay at home. A lighter suitcase makes for a faster journey and fewer surprises at the other end.

i
Practical Tip

Always include things like version-control folders, local settings files, private keys, and temporary build outputs in your ignore file. It makes builds faster and keeps sensitive files out of your images.

8What Happens When Docker Builds a Dockerfile

Watching the build from the inside shows why each instruction matters.

When you start a build, the Docker builder reads the Dockerfile and works through the instructions in order. For each instruction that changes files, the builder runs the step in a temporary environment, records exactly which files changed, and saves that record as a new layer. Instructions that only record settings, such as the start command, are stored in the image’s configuration. At the end, the builder seals everything together into a finished image and gives it a name.

Modern Docker uses a builder called BuildKit, which is smarter and faster than older ones. It can build independent steps at the same time, skip work whose results are already remembered, and handle sensitive values more safely. Most of the time you do not need to think about it, but it is the engine that makes builds feel quick.

sequenceDiagram
  participant You as You
  participant CLI as Docker CLI
  participant D as Docker builder
  participant Reg as Registry
  You->>CLI: Ask to build from the Dockerfile
  CLI->>D: Send Dockerfile and build context
  D->>Reg: Fetch the base image layers if missing
  Reg-->>D: Send base layers
  D->>D: Run each instruction and save layers
  D-->>CLI: Report the finished image
  CLI-->>You: Image is ready with its name and tag
        
FIG 3 — The conversation between you, the Docker tools, and the builder during an image build
Step 1

Read and Plan

The builder reads the Dockerfile, works out the order of steps, and decides which can be skipped or run together.

Step 2

Fetch the Base

The base image’s layers are downloaded if they are not already on the machine.

Step 3

Execute and Save

Each file-changing instruction runs, and its resulting changes are stored as a layer.

Step 4

Seal and Name

The layers and settings are combined into a finished image, which receives its name and version label.

9Layers and the Build Cache

A well-written Dockerfile builds in seconds because it makes good use of the cache.

Every file-changing instruction in a Dockerfile produces a layer, and Docker remembers these layers. The next time you build, Docker compares each instruction with its memory. If an instruction and everything before it are unchanged, Docker reuses the stored layer and skips the work. This is called a cache hit, and it is the reason a second build can be dramatically faster than the first.

There is one crucial rule. As soon as one instruction changes, that layer and every layer after it must be rebuilt, because each layer sits on top of the ones before. So the cache only helps as an unbroken chain from the top of the file down to the first change.

flowchart TD
  A["Instruction 1: choose base"] --> B{"Unchanged?"}
  B -- Yes --> C["Reuse cached layer"]
  B -- No --> X["Rebuild this and everything after"]
  C --> D["Instruction 2: install dependencies"]
  D --> E{"Unchanged?"}
  E -- Yes --> F["Reuse cached layer"]
  E -- No --> X
  F --> G["Instruction 3: copy your code"]
  G --> H{"Files changed?"}
  H -- Yes --> Y["Rebuild only this and later steps"]
  H -- No --> Z["Reuse cached layer"]
        
FIG 4 — The build cache reuses layers until it meets the first changed instruction

The Golden Rule of Ordering

Put the steps that rarely change at the top and the steps that change often at the bottom. Installing software and dependencies changes rarely, so it belongs near the top. Your own code changes many times a day, so it belongs near the bottom. That way, an everyday code edit only rebuilds the last few layers, and the slow steps come straight from the cache.

Simple Analogy

When you paint a house, you finish the strong, slow foundation first and do the colorful finishing touches last. If you decide to change the wall color, you repaint the walls, but you never have to lay the foundation again. A good Dockerfile is arranged the same way.

10The Three Confusing Twins: RUN, CMD, and ENTRYPOINT

Beginners often mix up these three, because all of them involve running commands. The difference is when they run.

The key question is: “At what moment does this command happen?” One of them happens while the image is being built. The other two happen only later, when a container starts. Once you attach the correct moment to each, the confusion disappears.

InstructionWhen It RunsWhat It DoesEffect on the Image
RUNDuring the buildExecutes a command, such as installing softwareSaves the changes as a new layer
CMDWhen a container startsProvides the default command or default argumentsRecorded as a setting; easy to override
ENTRYPOINTWhen a container startsDefines the main program the container always runsRecorded as a setting; harder to override on purpose

Using CMD and ENTRYPOINT Together

The two can cooperate. The entrypoint names the fixed program, and the command supplies its default arguments. Anyone starting the container can then replace just the arguments without touching the program. For example, an image built to be a converter tool might always run the converter program, while the default arguments say which file to convert, and the user can supply a different file at start-up.

Only the last CMD in a Dockerfile takes effect. If you write several, the earlier ones are simply ignored. This surprises people who expect all of them to run.

Simple Analogy

RUN is like the preparation done in the factory before a toy is boxed. CMD is like the default setting printed on the toy’s instruction card, which the owner can change. ENTRYPOINT is like the toy’s fixed purpose: a toy car is always a car, whatever settings you choose.

!
Common Mistake

Trying to start a server with RUN. That instruction happens at build time, and the server would only run during the build and then stop. The step that starts your application belongs in CMD or ENTRYPOINT, because those describe what happens when the container starts.

11Other Look-Alikes: COPY vs ADD and ARG vs ENV

Two more pairs of similar-looking instructions deserve a quick explanation.

COPY Versus ADD

Both bring files into the image. COPY does exactly one simple thing: it copies what you name from the build context. ADD does that too, but it has extra abilities, such as downloading from a web address and automatically unpacking some kinds of archives. Because those extras can cause surprises, the common advice is to prefer COPY for ordinary work and use ADD only when you truly need its special powers.

ARG Versus ENV

Both hold values, but they live for different lengths of time. ARG values exist only while the image is being built. They are useful for choosing a version or a build option, and they vanish afterwards. ENV values are stored in the image and remain visible to the container while it runs. Use ARG for build-time choices and ENV for settings the running application needs.

When to Choose Each

  • COPY for everyday file copying from the build context
  • ARG for options that only matter while building
  • ENV for defaults that the running application reads

Watch Out For

  • ADD downloading or unpacking things you did not intend
  • Putting secrets in ARG or ENV, where they can be seen later
  • Assuming ARG values are available when the container runs
!
Secrets Are Not Safe Here

Values passed through ARG or ENV can often be read back from the finished image’s history or settings. Never treat them as a secure hiding place for passwords or keys.

12Choosing a Base Image Wisely

The first instruction you write affects almost everything that follows.

The base image is the foundation of your stack. It decides how big your image is, how many programs are pre-installed, how easy it is to work with, and how many security weaknesses you inherit. Choosing well at the start saves effort for the whole life of the image.

Option 1

Full Operating-System Image

A familiar, feature-rich base with many tools included. Easy to work with, but large and carrying more software than most applications need.

Option 2

Slim Variant

A trimmed-down version of a language or system image. A good balance between convenience and size for many projects.

Option 3

Minimal Distribution

A tiny operating-system base, only a few megabytes. Very small, though some software may need adjustments to run on it.

Option 4

Distroless Image

Contains only your application and what it needs to run, with no shell or package tools. Very small and harder to attack.

Option 5

Scratch (Empty)

A completely empty starting point, used for programs that are entirely self-contained. The smallest possible foundation.

How to Decide

Prefer official images from sources you trust, because they are maintained and patched by experts. Prefer smaller bases when your application allows it, because fewer programs mean fewer possible weaknesses and faster downloads. And name a specific version rather than relying on a moving label like “latest,” so that your build gives the same answer next month as it does today.

i
Practical Tip

A moving label can silently change under your feet. Pinning an exact version, or even an image digest, makes your build repeatable and protects you from sudden surprises when a base image is updated.

13Multi-Stage Builds: Cooking in One Kitchen, Serving in Another

A single Dockerfile can contain several stages, and only the last one becomes the final image.

Building an application often needs heavy tools, such as compilers and testing programs, that are useless once the application is finished. If you leave them in the final image, it becomes large and full of unnecessary software. A multi-stage build fixes this by splitting the Dockerfile into stages. An early stage has all the heavy tools and produces the finished application. A later stage starts from a small, clean base and copies in only the finished result. Everything else is discarded.

flowchart LR
  A["Stage 1: large image with compilers and tools"] --> B["Build and test the application"]
  B --> C["Finished application files"]
  C -->|copy only the result| D["Stage 2: small clean base"]
  D --> E["Final image: lean and secure"]
  A -.->|discarded| F["Heavy tools and temporary files"]
        
FIG 5 — A multi-stage build keeps the heavy tools in early stages and ships only the finished result

Advantages

  • Much smaller final images that download and start quickly
  • Fewer programs inside, so fewer security weaknesses
  • Build tools and source code never reach production
  • One Dockerfile handles both building and packaging

Trade-offs

  • The Dockerfile is a little longer and needs more careful thought
  • The final image has fewer tools, which makes on-the-spot debugging harder
  • You must copy exactly the right files between stages
Simple Analogy

A restaurant cooks in a busy, messy kitchen but serves in a clean dining room. Guests never see the pots, peelings, and knives. They receive only the finished dish. Multi-stage builds send only the finished dish to your customers.

14Security Inside the Dockerfile

Many security decisions are made in the recipe, long before any container runs.

Because a Dockerfile decides what goes into an image and how the container will behave, it is the best place to make an application safer. A few careful choices here protect every container built from the image.

Defense 1

Run as an Ordinary User

Create a normal user account and switch to it, so a break-in gains far less power than it would as an administrator.

Defense 2

Keep Secrets Out

Never write passwords or keys into the Dockerfile or copy them into layers. Supply them safely at build time or run time.

Defense 3

Use Small Trusted Bases

Fewer packages mean fewer weaknesses. Prefer official, well-maintained images from sources you trust.

Defense 4

Pin Versions

Name exact versions of bases and packages so that nothing changes without you deciding it.

Defense 5

Install Only What You Need

Skip optional extras. Every additional program is one more thing that may contain a security bug.

Defense 6

Scan the Result

Run a vulnerability scanner on the finished image and rebuild regularly to pick up fixes.

ANTI-PATTERN-01 Avoid
Problem

Writing a password or access key directly into the Dockerfile, or copying a private key file into the image and deleting it in a later step.

Why It’s Harmful

Layers are permanent. The secret stays inside an earlier layer and in the file’s history, so anyone who gets the image or the file can read it.

Correct Approach

Keep secrets out of the recipe entirely. Use the build system’s secret-handling feature for build-time needs, and inject runtime secrets when the container starts.

15Checking Quality: Linting, Logs, and Health

A Dockerfile is code, and like any code it benefits from checking.

Because a Dockerfile is a text file, tools can read it and point out mistakes before you ever build. Community tools known as linters scan a Dockerfile for common problems, such as unpinned versions, missing cleanup, or risky habits, and suggest improvements. Adding a linter to your automated checks is an easy way to keep quality high across a team.

During and after a build, several signals help you understand what you produced. The build output shows each step, how long it took, and whether it came from the cache. An image history view lists every layer and how many bytes it added, which is the fastest way to find a bloated step. A vulnerability scan reports known weaknesses in the packages inside the image.

Check 1

Linting

Reads the Dockerfile as text and flags risky or wasteful patterns before you build.

Check 2

Build Output

Shows each step, its timing, and whether the cache was used, revealing why a build is slow.

Check 3

Image History

Lists every layer with its size, pointing straight at the steps that add the most weight.

Check 4

Vulnerability Scans

Reports known security problems in the software packed into the image.

Check 5

Health Checks

A HEALTHCHECK instruction lets Docker keep testing whether a running container is truly working.

i
Helpful Habit

Whenever an image feels too big or a build feels too slow, look at the history and the build output first. The culprit is almost always one or two steps, and fixing them gives a big improvement.

16Strengths and Trade-offs of Using Dockerfiles

Writing the recipe as a file brings real benefits, along with a few costs.

Advantages

  • Environments become repeatable: same recipe, same result, on any machine
  • The whole setup is documented in one readable file
  • It can be stored in version control, reviewed, and improved by a team
  • Builds can be automated and run by pipelines without human effort
  • The cache makes repeat builds fast
  • New team members can start working in minutes, not days

Disadvantages / Trade-offs

  • Beginners must learn the instruction vocabulary and the caching rules
  • Poor ordering makes builds slow and images large
  • Layers are permanent, so mistakes such as leaked secrets are hard to undo
  • A carelessly written Dockerfile can quietly bake in outdated software
  • Very complex setups can turn into long, hard-to-read files
“A Dockerfile turns tribal knowledge into a file that anyone can read, run, and improve.”

17Dockerfile Compared With Its Neighbors

A table to keep the Dockerfile separate from other things it is often confused with.

ThingWhat It IsJobWhen It Is Used
DockerfileA text recipeDescribes how to build one imageAt build time
ImageA read-only packaged resultHolds the application and its dependenciesStored and shared after the build
ContainerA running instance of an imageActually runs the applicationAt run time
Ignore fileA list of files to leave outKeeps unneeded files out of the build contextAt build time
Compose fileA separate text file describing several containers togetherStarts and connects multiple services at onceAt run time, for multi-container setups
Shell scriptA list of commands run on a machineAutomates steps on an existing computerWhenever the script is executed

Dockerfile Versus a Shell Script

A shell script performs steps on a computer that already exists, and the outcome depends on what state that computer was in. A Dockerfile builds from a clean, known starting point every time, and produces a sealed image as its output. That difference is what makes Dockerfile results so predictable.

Dockerfile Versus a Compose File

A Dockerfile describes how to build one image. A Compose file, which is a different file, describes how to run several containers together, such as a web application, a database, and a cache. They often work as a team: the Compose file points to Dockerfiles for the images it needs to build.

18Real-World Examples

Dockerfiles appear wherever teams want reliable, repeatable software packaging.

A New Developer’s First Day

A new team member clones the project, and the Dockerfile builds the exact environment the rest of the team uses. Instead of spending days installing tools and fixing version clashes, they are productive within the hour.

An Automated Build Pipeline

Every time a developer saves code, an automation system reads the Dockerfile, builds a fresh image, runs tests against it, and publishes it if all goes well. The Dockerfile makes this fully hands-off and consistent.

A Company Base Image

A security team writes one carefully hardened Dockerfile that produces an approved base image. Other teams start their own Dockerfiles from it, inheriting good defaults, shared layers, and quick security updates.

A Reproducible Data Job

A data team packages an analysis tool with all its libraries in a Dockerfile. Months later, they can rebuild the identical environment to reproduce an old result exactly, something that would be nearly impossible with hand-installed software.

A Teaching Environment

An instructor shares a Dockerfile with a class. Every student gets an identical setup, so lessons never stall because one person’s laptop has a different version of a tool.

19Patterns and Anti-Patterns

Some habits make Dockerfiles a pleasure to maintain. Others create slow builds and heavy images.

Helpful Patterns

Stable Steps First, Changing Steps Last

Arrange the file so rarely changing work sits at the top and frequently edited files are copied last. The cache then does most of the work for you.

Dependencies Before Code

Copy only the file listing your dependencies first, install them, and only then copy the rest of your code. Ordinary edits then never trigger a slow reinstall.

One Purpose per Image

Give each image a single job, such as one web service or one worker. Small, focused images are easier to build, scan, and replace.

Anti-Patterns to Avoid

ANTI-PATTERN-02 Avoid
Problem

Copying the entire project folder into the image as one of the first steps, before installing dependencies.

Why It’s Harmful

Any small edit to any file invalidates the cache from that point on, forcing slow dependency installation on every single build.

Correct Approach

Copy the dependency list first and install, then copy the remaining code afterwards so that ordinary edits only affect the final layers.

ANTI-PATTERN-03 Avoid
Problem

Downloading a large temporary file in one step and deleting it in a separate later step, expecting the image to become smaller.

Why It’s Harmful

Deleting in a later layer only hides the file. Its bytes remain in the earlier layer, so the image stays large.

Correct Approach

Download, use, and clean up inside the same step, or use a multi-stage build so temporary files never reach the final image.

ANTI-PATTERN-04 Avoid
Problem

Relying on the moving label “latest” for the base image, and running everything as an administrator.

Why It’s Harmful

Builds change silently over time and become unrepeatable, and any break-in gains full power inside the container.

Correct Approach

Pin an exact version or digest for the base image, and switch to an ordinary user before the application starts.

ANTI-PATTERN-05 Avoid
Problem

Packing a whole “mini server” into one image: the application, a database, a web server, and a scheduler, all started together.

Why It’s Harmful

The image becomes huge and hard to update, one failing part affects everything, and you cannot scale the pieces independently.

Correct Approach

Build separate images for separate jobs and connect the resulting containers, for example with a Compose file.

20Best Practices Checklist

A short list to help you write Dockerfiles that are small, fast, and safe.

1

Start from a small, trusted, pinned base

Choose an official image, keep it as small as your application allows, and name an exact version.

2

Order instructions from stable to volatile

Put slow, rarely changing work first and your own frequently edited code last to get the most out of the cache.

3

Use an ignore file

Keep version-control folders, local settings, and private files out of the build context.

4

Clean up in the same step that makes the mess

Remove temporary files inside the same instruction that created them so they never get saved into a layer.

5

Use multi-stage builds

Build in one stage and ship from another, so heavy tools and source code stay out of the final image.

6

Run as an ordinary user

Switch away from the administrator account before the application starts.

7

Keep secrets out of the recipe

Never write passwords or keys into a Dockerfile, an argument, or an environment value.

8

Lint, scan, and rebuild regularly

Check the file with a linter, scan the finished image, and rebuild on a schedule to pick up security fixes.

9

Add comments and keep it readable

A short note explaining an unusual step helps the next person, who might be you in six months.

21When a Build Fails: A Way of Thinking

Build problems feel scary at first, but they usually fall into a few familiar groups.

When a build fails, Docker stops at the exact instruction that went wrong and shows the message from that step. That is the biggest clue you will get, so always read the error from the step where it stopped rather than from the very top. Asking the right questions in a sensible order usually leads to the cause quickly.

flowchart TD
  A["Build failed"] --> B{"Which instruction stopped?"}
  B --> C{"Is it a file-copy step?"}
  C -- Yes --> D["Check the build context and the ignore file for the missing file"]
  C -- No --> E{"Is it an install step?"}
  E -- Yes --> F["Check network access, package names, and version numbers"]
  E -- No --> G{"Is it the base image step?"}
  G -- Yes --> H["Check the image name, tag, and registry access"]
  G -- No --> I["Read the full error message and the earlier steps for clues"]
        
FIG 6 — A simple thinking path for diagnosing a failed build
Symptom 1

File Not Found During Copy

The file is outside the build context, or the ignore file is excluding it. Check both.

Symptom 2

Package Install Fails

Often a network problem, a misspelled package, or a version that no longer exists.

Symptom 3

Build Is Suddenly Slow

An early instruction probably changed and broke the cache chain. Look at what changed near the top.

Symptom 4

Container Exits Immediately

The start command may finish at once or fail. Check the CMD or ENTRYPOINT and the container’s logs.

i
Troubleshooting Habit

Always ask, “At which instruction did it fail, and was that a build-time problem or a run-time problem?” Separating those two moments removes most of the guesswork.

22Frequently Asked Questions

Q1What is a Dockerfile in one sentence?

A Dockerfile is a plain text file of ordered instructions that tells Docker how to build an image, including which base to use, which software and files to add, and what to run at start-up.

Q2Is a Dockerfile the same as an image?

No. The Dockerfile is the recipe, and the image is the finished result of following it. A container is then a running copy made from the image.

Q3Why is the file named Dockerfile with no extension?

It is simply the default name Docker looks for. You may use a different name if you tell Docker which file to read, but the standard name keeps things simple and familiar.

Q4Does every line in a Dockerfile create a layer?

No. Instructions that change files, such as running a command or copying files, create layers. Instructions that only record settings add information to the image’s configuration without adding meaningful file content.

Q5What is the difference between RUN and CMD?

RUN executes during the build and saves its results into the image. CMD sets the default command that runs later, when a container starts. Build-time work belongs in RUN, and start-up behavior belongs in CMD or ENTRYPOINT.

Q6Do I need a Dockerfile to run a container?

No. You can run containers from existing images, such as those from a public registry, without ever writing a Dockerfile. You write one when you want to build your own image.

Q7Why do my builds get slow after a small change?

Because a change to an early instruction invalidates the cache for every instruction after it. Reorder your file so stable steps come first and your own code is copied near the end.

Q8Can I put more than one CMD in a Dockerfile?

You can write more than one, but only the last one takes effect. The earlier ones are ignored, which surprises many beginners.

Q9How do I keep my images small?

Choose a small base image, install only what you need, clean up within the same step that creates temporary files, use an ignore file, and use multi-stage builds so heavy tools never reach the final image.

Q10Is a Compose file the same as a Dockerfile?

No. A Dockerfile describes how to build a single image. A Compose file describes how to run and connect several containers together. They are different files with different jobs, and they often work together.

23Summary and Key Takeaways

A Dockerfile is the written recipe behind every custom Docker image. It is a simple text file of ordered instructions: a base to start from, steps that add software and files, settings that describe the environment, and a command that says what runs when a container starts. Docker reads the file from top to bottom, turns each file-changing step into a layer, and seals the result as an image that can be shared and run anywhere. Because the recipe is text, it can be versioned, reviewed, linted, and automated, which turns fragile setup rituals into repeatable engineering. Good Dockerfiles order their steps to use the cache, keep secrets out, run as an ordinary user, and use multi-stage builds to ship only what is needed. Once you see the Dockerfile as the first link in the chain from recipe to image to container, the rest of Docker falls into place.

Key Takeaways

  • A Dockerfile is a recipe, not a result — it is a text file of instructions that Docker follows to build an image, and it does nothing until a build is run.
  • The chain is Dockerfile, image, container — the recipe builds an image, and the image starts containers.
  • Instructions are read from top to bottom — a base image comes first, and each later instruction builds on the results of the earlier ones.
  • RUN builds, CMD and ENTRYPOINT start — remember the moment each one happens: build time versus container start time.
  • Order controls speed — put stable steps first and fast-changing steps last so the build cache saves you time.
  • Small and safe wins — choose small trusted bases, use multi-stage builds, run as an ordinary user, and never put secrets in the file.
  • Treat it like code — keep it in version control, add comments, lint it, scan the results, and rebuild regularly.