Skip to content
Laviyx Learn

Try

Docker and Kubernetes course menu

Dockerfile Tutorial: Build Your Own Docker Image

Write a Dockerfile with FROM, WORKDIR, COPY, RUN, ENV, EXPOSE, CMD and ENTRYPOINT, build it with docker build, and understand the difference between RUN and CMD.

Beginner7 min readUpdated
On this page

A Dockerfile is a text file with the steps needed to build an image. It makes your environment repeatable, and it lives in Git with your code.

A first Dockerfile#

Here is a small website. index.html is the page:

Terminal
mkdir site
cd site
echo "<h1>Built with a Dockerfile</h1>" > index.html
cat > Dockerfile <<'EOF'
FROM nginx:1.30-alpine
COPY index.html /usr/share/nginx/html/index.html
EOF

FROM picks a base image, and COPY adds your file to it. Build it with docker build. -t names the image, and . means "use this folder":

Terminal
docker pull -q nginx:1.30-alpine > /dev/null
docker build --progress=plain -t my-site:1.0 . 2>&1
Output
#0 building with "default" instance using docker driver
#5 [1/2] FROM docker.io/library/nginx:1.30-alpine
#6 [2/2] COPY index.html /usr/share/nginx/html/index.html

Run it:

Terminal
docker run -d --name s1 -p 8080:80 my-site:1.0 > /dev/null
sleep 2
curl -s http://localhost:8080
docker rm -f s1 > /dev/null
Output
<h1>Built with a Dockerfile</h1>

Common instructions#

InstructionPurpose
FROMThe base image; the first instruction
WORKDIRSet the working folder; created if needed
COPYCopy files from your computer into the image
RUNRun a command while building
ENVSet an environment variable
EXPOSEDocument which port the app uses
USERChoose the user that runs the app
CMDThe default command when a container starts
ENTRYPOINTThe fixed program a container runs

A more complete example#

Terminal
cat > Dockerfile <<'EOF'
FROM alpine:3.24
RUN apk add --no-cache curl
WORKDIR /app
COPY greet.sh .
RUN chmod +x greet.sh
ENV GREETING="Hello"
CMD ["./greet.sh"]
EOF
cat > greet.sh <<'EOF'
#!/bin/sh
echo "$GREETING from $(curl --version | head -n 1 | cut -d' ' -f1)"
EOF
docker build -q -t greeter:1.0 . > /dev/null
docker run --rm greeter:1.0
docker run --rm -e GREETING=Namaste greeter:1.0
Output
Hello from curl
Namaste from curl

RUN and CMD#

They are often confused:

  • RUN happens once, during the build, and its result is saved in the image. Use it to install things.
  • CMD happens each time a container starts.

CMD and ENTRYPOINT#

CMD is a default that you can replace on the command line. ENTRYPOINT is fixed, and arguments are added after it:

Terminal
cat > Dockerfile <<'EOF'
FROM alpine:3.24
ENTRYPOINT ["echo", "Hello"]
CMD ["world"]
EOF
docker build -q -t hi:1.0 . > /dev/null
docker run --rm hi:1.0
docker run --rm hi:1.0 Laviyx
Output
Hello world
Hello Laviyx

Use CMD for most apps. Use ENTRYPOINT when an image behaves like one command.

Exec form and shell form#

CMD ["node", "app.js"] is the exec form, and it runs the program directly. CMD node app.js is the shell form, and it runs through sh. Prefer the exec form, because your app then receives stop signals, and can shut down cleanly.

COPY and ADD#

ADD can also unpack archives and fetch URLs, which makes it less predictable. Use COPY, unless you need those features.

Build arguments#

ARG passes a value at build time:

Terminal
cat > Dockerfile <<'EOF'
ARG VERSION=3.24
FROM alpine:${VERSION}
RUN cat /etc/alpine-release > /version.txt
CMD ["cat", "/version.txt"]
EOF
docker build -q -t ver:new . > /dev/null
docker build -q --build-arg VERSION=3.23 -t ver:old . > /dev/null
docker run --rm ver:new | cut -d. -f1,2
docker run --rm ver:old | cut -d. -f1,2
Output
3.24
3.23

ENV values remain in the running container, while ARG values exist only during the build. Do not pass secrets in either, because they can be read from the image.

Common mistakes#

Practice#

Practice exercise

Write a Dockerfile from alpine:3.24 that copies hello.txt to /data/hello.txt, and prints the file when a container starts. Build it as reader:1.0 and run it.

Show solution
Terminal
echo "Read me" > hello.txt
cat > Dockerfile <<'EOF'
FROM alpine:3.24
WORKDIR /data
COPY hello.txt .
CMD ["cat", "hello.txt"]
EOF
docker build -q -t reader:1.0 . > /dev/null
docker run --rm reader:1.0
Output
Read me

Key takeaway#

Next: Layers, Cache and .dockerignore.

Finished this lesson?

Some links are affiliate links. If you buy through them, Laviyx Learn may earn a commission at no extra cost to you. As an Amazon Associate, we earn from qualifying purchases.