Skip to content
PickNTech

Docker Fundamentals

Images, containers and Dockerfiles — and packaging a Spring Boot app in one.

Intermediate22 minUpdated 25 Sept 2026

What you’ll learn

  • Images vs containers
  • Essential docker commands
  • Writing a Dockerfile for a Java app
  • Multi-stage builds for smaller images

The problem Docker solves

"It works on my machine" usually means the machine has a different Java version, OS library or environment variable than production. Docker packages your app with its environment so it runs identically everywhere.

Images vs containers

  • An image is a read-only template: an OS base, a runtime, your app and its config. Think of it as a class.
  • A container is a running instance of an image. Think of it as an object.
Dockerfile→docker build→Image→docker run→Container

Essential commands

docker pull eclipse-temurin:21-jre       # download an image
docker images                            # list local images
docker run -d -p 8080:8080 --name api my-api   # run in background, map port
docker ps                                # running containers
docker logs -f api                       # follow logs
docker exec -it api sh                   # shell inside the container
docker stop api && docker rm api         # stop and remove

-p 8080:8080 maps host port : container port. Without it, your app runs but isn't reachable.

A Dockerfile for Spring Boot

Dockerfile
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY target/employee-api.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
./mvnw package -DskipTests
docker build -t employee-api .
docker run -p 8080:8080 employee-api

Multi-stage builds

The Dockerfile above requires you to build the JAR on your machine first. A multi-stage build compiles inside Docker, then copies only the JAR into a small runtime image:

Dockerfile
# Stage 1: build
FROM eclipse-temurin:21-jdk AS build
WORKDIR /src
COPY . .
RUN ./mvnw -q package -DskipTests
 
# Stage 2: run
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /src/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

The final image contains no JDK, no source code and no Maven cache — it's smaller and has less attack surface.

Tip

Add a .dockerignore with target/, .git/ and IDE folders so the build context stays small and builds stay fast.

Configuration via environment variables

Spring Boot maps environment variables to properties automatically — SPRING_DATASOURCE_URL becomes spring.datasource.url:

docker run -p 8080:8080 \
  -e SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/hr \
  -e SPRING_PROFILES_ACTIVE=prod \
  employee-api

Never bake secrets into an image.

⚡ Try it yourself

Containerise the REST API from the Spring Boot lessons with a multi-stage Dockerfile. Run it, call an endpoint, then change SERVER_PORT with -e and adjust the port mapping to match.

Interview questions

QWhat's the difference between a container and a virtual machine?+

A VM virtualises hardware and runs a full guest OS with its own kernel. A container shares the host's kernel and isolates processes with namespaces and cgroups, so it starts in milliseconds and uses far less memory.

QWhat's the difference between CMD and ENTRYPOINT?+

ENTRYPOINT defines the executable that always runs. CMD provides default arguments (or a default command) that can be overridden by arguments to docker run.

Key takeaways

  • Image = template, container = running instance.
  • Use multi-stage builds to ship small runtime images.
  • Configure with environment variables, never baked-in secrets.

Finished this lesson?

Mark it complete to move forward on your roadmap.