Chapter 3 of 16 · Visual guide

Dockerfile basics

A Dockerfile is a recipe. Every step becomes a layer, and CMD decides what runs when the container starts.

key idea / Docker itselfcontainer / good pathhost / watch outproblem / blockedneutral

From recipe to layers

Docker executes the file top to bottom and saves the result of each step as a layer.

Dockerfile to layersEach Dockerfile instruction that changes files becomes one layer. CMD only records metadata.DockerfileImage layersFROM alpine:3.20layer 1: base OS filesWORKDIR /applayer 2: empty /app directoryCOPY hello.txt .layer 3: hello.txtRUN printf ... > built.txtlayer 4: built.txtCMD ["cat", "hello.txt"]metadata only, no files
Read the Dockerfile top to bottom; the image stacks the same steps bottom to top.

Build time versus run time

The confusion between RUN and CMD disappears once you separate the two moments.

Build time versus run timedocker build (once)FROM · COPY · RUNexecuted, results saved as layersImageready to shipdocker run (every start)CMD / ENTRYPOINTbecomes the main processContainerPID 1 is that processimage
RUN and COPY happen once, at build. CMD happens every time a container starts.
CMD versus ENTRYPOINTWith CMD, extra words on docker run replace it. With ENTRYPOINT, they become its arguments.CMD ["cat", "hello.txt"]docker run imgcat hello.txtdocker run img lsls (CMD replaced)your words replace the defaultENTRYPOINT ["ping"]docker run img hping hdocker run img -c 1 hping -c 1 hyour words become arguments

The instructions you will use daily

InstructionJobCan be replaced by docker run image ...?
RUNruns at build time, adds a layerno, already baked in
CMDdefault command at startyes: whatever you type replaces it
ENTRYPOINTfixed program at startno, your words become its arguments
COPYhost files into the imageno (use -v for runtime files)
ADDCOPY plus URLs and tar extractprefer COPY; ADD surprises people

Try it yourself

Work in an empty directory. Each instruction below becomes one layer.

  1. Step 1 · Write the files
    $ mkdir greet && cd greet
    $ printf 'hello from the image\n' > hello.txt
    $ cat > Dockerfile << 'EOF'
    FROM alpine:3.20
    WORKDIR /app
    COPY hello.txt .
    RUN printf 'built in the image\n' > /app/built.txt
    CMD ["cat", "/app/hello.txt"]
    EOF
  2. Step 2 · Build, and read each step

    The numbers are the Dockerfile instructions. FROM downloads Alpine. WORKDIR creates /app. COPY adds hello.txt. RUN writes built.txt into the image.

    $ docker build -t greet:1 .
    [1/4] FROM docker.io/library/alpine:3.20
    [2/4] WORKDIR /app
    [3/4] COPY hello.txt .
    [4/4] RUN printf 'built in the image\n' > /app/built.txt
    naming to docker.io/library/greet:1
  3. Step 3 · CMD is the default process
    $ docker run --rm greet:1
    hello from the image
  4. Step 4 · A command on docker run replaces CMD

    The file from RUN is in the image, so this container can read it. --rm deletes the container when cat exits. The image stays.

    $ docker run --rm greet:1 cat /app/built.txt
    built in the image
    $ docker images greet
    REPOSITORY   TAG   IMAGE ID       SIZE
    greet        1     9f0c1a2b3c4d   8MB

Recap

Quick check

A Dockerfile has two CMD lines. What runs?
Show answer

Only the last one. Earlier CMD lines are ignored. Use ENTRYPOINT when the command must not be replaced by arguments.