Topic 2.1
Your First Dockerfile
In one line
A Dockerfile is a plain-text recipe, read top to bottom, where each instruction produces exactly one new layer in the resulting image.
Think of it like this
A recipe card. 'Preheat oven' (base setup), 'add flour, sugar, eggs' (add your files), 'mix' (run a build command), 'bake at 350°F' (the command that runs when you start it). Docker reads your Dockerfile top to bottom exactly like a recipe, one step at a time, in order.
Key ideas
- 01
FROM— every Dockerfile starts here, picking a BASE image to build on top of (e.g.FROM eclipse-temurin:17-jre). You're never starting from nothing; you're always extending an existing image. - 02
COPY— copies files from your local machine (the 'build context') into the image's filesystem.COPY target/app.jar app.jarcopies a built JAR into the image at pathapp.jar. - 03
RUN— executes a command DURING the build (not when the container later starts) — e.g.RUN apt-get update && apt-get install -y curlinstalls a package into the image itself, once, at build time. - 04
CMD— specifies the DEFAULT command that runs when a container is STARTED from this image (not during build). E.g.CMD ["java", "-jar", "app.jar"]. There can be only one CMD (the last one wins if you accidentally write several); it can be overridden atdocker runtime by appending a command. - 05
WORKDIR— sets the working directory for all subsequent instructions (likecdthat persists for the rest of the file) — cleaner than repeating full paths in every RUN/COPY/CMD. - 06
EXPOSE— pure DOCUMENTATION that a container listens on a given port; it does NOT actually publish the port to your host (that's-patdocker runtime, Phase 4) — many beginners mistakenly think EXPOSE alone makes a port reachable. - 07
Build the image with
docker build -t myapp:v1 .— the.is the BUILD CONTEXT (the directory Docker can see and copy files from), and-ttags the resulting image with a name.
In your stack
- →
The minimum viable Dockerfile for a Spring Boot app, assuming you've already run
mvn packagelocally to produce a JAR: FROM a JRE (not a full JDK — you only need to RUN Java, not compile it) base image, COPY the jar in, CMD to run it. Phase 7 improves on this significantly with multi-stage builds that also handle themvn packagestep INSIDE Docker.
Code & diagrams
The simplest real Dockerfile for a Java app — five instructions, each doing exactly one job.
# 1. Start from an official, minimal Java runtime (not a full JDK — we only RUN, not compile)
FROM eclipse-temurin:17-jre
# 2. Set the working directory for everything below
WORKDIR /app
# 3. Copy the already-built JAR from your local machine into the image
COPY target/myapp-0.0.1.jar app.jar
# 4. Document (not publish!) the port this app listens on
EXPOSE 8080
# 5. The default command when a container starts from this image
CMD ["java", "-jar", "app.jar"]# Build the image, tagging it "myapp:v1", using the current directory as build context
docker build -t myapp:v1 .
# Run a container from the image you just built, mapping the port to your host
docker run -d --name myapp -p 8080:8080 myapp:v1
# Confirm it's running and check its logs
docker ps
docker logs myappExplain it without notes
Why does EXPOSE 8080 in a Dockerfile NOT let you actually reach the app at localhost:8080 from your browser?
What's the difference between RUN and CMD — specifically, WHEN each one executes?
Practice
Write a Dockerfile for a simple static website: FROM nginx, COPY your HTML files into nginx's default serving directory (/usr/share/nginx/html), and build+run it.
Deliberately write TWO CMD instructions in one Dockerfile, build it, and confirm which one actually takes effect when you run a container.
Trade-offs
- ↔
Starting FROM a full JDK image instead of a JRE-only image works fine functionally but bloats your final image with an entire compiler toolchain you'll never use at runtime — a wasted few hundred MB that Phase 7's multi-stage builds specifically eliminate.
Done when you can
I can write a working Dockerfile for a simple app from memory.
I know exactly when RUN executes versus when CMD executes.
I understand EXPOSE is documentation only, not port publishing.