Every microservice is a small, independent program, and the hardest part of running many of them is that each wants its own Java version, its own libraries, its own configuration. Docker solves this by wrapping each service in a container: a self-contained box that carries the app and everything it depends on, so it runs identically on your laptop, a teammate's Mac and a production server. "Works on my machine" stops being an excuse when the machine travels with the code.
If you already understand why microservices are split into small deployable units, Docker is the tool that makes each of those units genuinely independent. This tutorial takes you from images and containers to a real Dockerfile and a multi-service Compose file.
Images and containers: the mental model
Two words dominate every Docker conversation, and mixing them up is the classic beginner error.
An image is a read-only template — a frozen snapshot of a filesystem containing your app, the Java runtime, and any libraries. You build it once. A container is a running instance of that image, with its own isolated process and writable layer on top.
The cleanest analogy borrows from Java itself: an image is like a class, and a container is like an object created from it. One image can spawn dozens of identical containers, exactly as one class can create dozens of objects. Scaling a microservice to handle more traffic means starting more containers from the same image.
| Concept | What it is | Analogy |
|---|---|---|
| Image | Read-only template of app + dependencies | A class |
| Container | Running instance of an image | An object |
| Dockerfile | Recipe to build an image | Source code |
| Registry | Store of shared images (e.g. Docker Hub) | A library repository |
Your first commands
With Docker installed, three commands cover most of daily use. docker build turns a Dockerfile
into an image, docker run starts a container from an image, and docker ps lists what is
currently running.
# pull and run a ready-made image to confirm Docker works
docker run hello-world
# list running containers
docker ps
# list all images on your machine
docker images
# stop a container by its id or name
docker stop <container-id>
docker run hello-world downloads a tiny image from Docker Hub, runs it, prints a success
message and exits. If you see that message, your installation is healthy and you are ready to
containerise something real.
Writing a Dockerfile for a Spring Boot service
A Dockerfile is the recipe Docker follows to build your image. Here is a production-minded Dockerfile for a Spring Boot microservice using a multi-stage build: the first stage compiles the code, the second stage keeps only the finished jar so the final image stays small.
# ---- build stage ----
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline # cache dependencies as a layer
COPY src ./src
RUN mvn clean package -DskipTests
# ---- run stage ----
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Each instruction becomes a cached layer. Because dependencies are copied and downloaded before the source code, changing your Java files does not force Maven to re-download every library — a small ordering trick that turns a five-minute rebuild into a ten-second one.
Build and run it:
docker build -t order-service:1.0 .
docker run -p 8080:8080 order-service:1.0
The -p 8080:8080 flag maps port 8080 inside the container to port 8080 on your machine, so you
can hit the service at http://localhost:8080. If you have built a service by following the
Spring Boot guide, you can containerise that exact project right now.
Pro tip: Use a slim base image like
eclipse-temurin:21-jre-alpinefor the run stage. A full JDK image can be 500 MB or more; a JRE-on-Alpine image is often under 200 MB. Across dozens of microservices, that difference is faster deploys, cheaper storage and a smaller attack surface.
Running several services with Docker Compose
A single container is easy. Real microservices come in groups: your service usually needs a database and often a message broker. Docker Compose lets you declare the whole set in one YAML file and start it with a single command.
services:
order-service:
build: .
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/orders
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: orders
POSTGRES_PASSWORD: secret
ports:
- "5432:5432"
Run docker compose up and Compose builds your service, pulls Postgres, creates a private
network, and connects them. Notice the datasource URL uses the hostname db, not localhost —
inside the Compose network, each service is reachable by its service name. That built-in service
discovery is why Compose is the standard way to run microservices locally.
Common mistake: Beginners hardcode
localhostin the datasource URL and wonder why the container cannot reach the database. Inside Docker,localhostmeans the container itself, not your host machine or a sibling container. Use the service name from the Compose file as the hostname instead.
Why containers fit microservices so well
The microservices style creates a real problem: dozens of independently deployed programs, possibly written in different languages, each with its own dependency tree. Running them all on one host without isolation invites version clashes — service A needs library 1.2, service B needs 1.5, and they fight over the shared machine.
Containers give each service a private, reproducible environment. Service A ships with library 1.2 baked into its image; service B ships with 1.5 in its own image; they never see each other's dependencies. This isolation is what lets teams own and deploy their service on their own schedule, which is the entire promise of microservices over a monolith.
Portability is the second win. The image you build and test on your laptop is byte-for-byte the image that runs in production. Nothing about the host OS leaks in, so an entire class of environment-specific bugs disappears.
From Docker to orchestration
Docker packages and runs containers, but it does not by itself decide which machine runs a container, restart a crashed one, or spread ten copies across a cluster. That job belongs to an orchestrator, and in industry that means Kubernetes.
Think of the split like this: Docker is how you build and run one container; Kubernetes is how you run thousands of them reliably across many servers, with automatic scaling, health checks and rolling updates. You almost always learn Docker first and Kubernetes second, because a Kubernetes deployment is built from the very images your Dockerfile produces. If you want the full breakdown, the Docker vs Kubernetes comparison lays out exactly where one ends and the other begins.
Common interview angles
Docker questions for freshers rarely go deep, but they do check whether you understand the core model. Be ready to explain the image-versus-container distinction crisply, describe what a Dockerfile does, and say why containers suit microservices. A frequent follow-up is "why not just run everything on one server?" — the answer is dependency isolation and independent deployability.
If you can also mention multi-stage builds keeping images small and Compose wiring services together locally, you sound like someone who has actually built with Docker rather than only read about it. Rehearse these points alongside the broader microservices interview questions set.
Practise on a real project
Reading about Docker teaches you the words; containerising your own service teaches you the skill. Take any Spring Boot app, add the Dockerfile above, build the image, and run it. Then add a Postgres service in a Compose file and watch the two containers talk over the private network.
In the Java Full Stack with AI program at CodeBegun you do exactly this on a multi-service project — building images, running them with Compose, and later handing those same images to an orchestrator. The concepts here are the foundation everything else in modern backend deployment is built on, so it is worth getting the mental model of images and containers rock solid before moving on to the rest of the microservices learning track.
Frequently Asked Questions
What is the difference between a Docker image and a container?
Why is Docker so useful for microservices specifically?
What does a Dockerfile do?
What is Docker Compose used for?
Is Docker the same as Kubernetes?
Do I need Docker installed to learn it?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — Explore the Program

