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.
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:
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
EOFFROM 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":
docker pull -q nginx:1.30-alpine > /dev/null
docker build --progress=plain -t my-site:1.0 . 2>&1#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.htmlRun it:
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<h1>Built with a Dockerfile</h1>Common instructions#
A more complete example#
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.0Hello from curl
Namaste from curlRUN and CMD#
They are often confused:
RUNhappens once, during the build, and its result is saved in the image. Use it to install things.CMDhappens 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:
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 LaviyxHello world
Hello LaviyxUse 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:
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,23.24
3.23ENV 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
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.0Read meKey takeaway#
Finished this lesson?
Related lessons
Recommended Docker and Kubernetes resources
- Docker Deep Dive by Nigel Poulton
A practical guide to how Docker works, for readers who have finished the basics.
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.