βHow do you build a Docker image for a Java microservice?β
β Step-by-Step: How to Build a Java Microservice Docker Image
Letβs assume your Java microservice is a Spring Boot application, and you have a jar file after building it.
πΉ 1. Build the JAR
Use Maven or Gradle to build your application:
Maven:
mvn clean package
This generates a target/my-service.jar.
πΉ 2. Create a Dockerfile
At the root of your project (next to pom.xml), create a file named Dockerfile:
# Start from a lightweight Java runtime base image
FROM openjdk:17-jdk-slim
# Set a working directory inside the container
WORKDIR /app
# Copy the built jar from local machine to the container
COPY target/my-service.jar app.jar
# Expose the port the app listens on (optional, documentation only)
EXPOSE 8080
# Run the application
ENTRYPOINT ["java", "-jar", "app.jar"]
πΉ 3. Build the Docker Image
docker build -t my-service:latest .
This command:
- Reads the
Dockerfile - Builds the image
- Tags it as
my-service:latest
πΉ 4. Run the Docker Container
docker run -p 8080:8080 my-service:latest
Now your Java microservice runs in a container, accessible at localhost:8080.
π Interview Tips β Explain Concepts Too
When asked this, go beyond steps and explain:
πΈ Why use Docker?
- To containerize the microservice for consistent deployment across environments.
- Makes it portable and works the same way in dev, test, and prod.
πΈ Base Image Choice?
openjdk:17-jdk-slimis small and optimized.- For GraalVM or native builds, use
distrolessoralpine(advanced use cases).
πΈ Multi-Stage Build (Optional for Clean Images)
# Build stage
FROM maven:3.9.5-eclipse-temurin-17 as build
WORKDIR /build
COPY . .
RUN mvn clean package -DskipTests
# Run stage
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY --from=build /build/target/my-service.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
π’ This avoids needing to copy the .jar manually after building β it builds and packages in one go.
β Summary Answer for Interview
βTo containerize a Java microservice, I first build the jar using Maven or Gradle. Then I write a
Dockerfilethat starts from a slim OpenJDK base image, copies the jar, and runs it. I usedocker buildto build the image anddocker runto launch it. For better optimization, I sometimes use a multi-stage build to avoid copying unneeded files and keep the final image clean and lightweight.β
β 1. Docker image should only contain your service
- Your Docker image should package only your microservice code and its dependencies (like the JAR file, configs, etc.).
- The database is a separate concern and usually runs in its own container or is managed by a database-as-a-service platform (like AWS RDS).
β 2. Microservices should not tightly couple the DB into the container
Reasons why DB should be external:
| Reason | Explanation |
|---|---|
| π Reusability | Multiple services might connect to the same DB. You donβt want to embed it in each one. |
| βοΈ Scalability | DBs have different scaling patterns than microservices. They require different infra. |
| π Security | DB access can be locked down separately (firewalls, IAM, etc.). |
| π Configurability | DB credentials/URLs should come from env vars or config maps, not hardcoded. |
β Correct Way: Use Environment Variables or Config Files for DB Connection
Let your app pick up DB config using application.properties or env variables like:
spring.datasource.url=${DB_URL}
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASS}
And pass those while running the container:
docker run -e DB_URL=jdbc:mysql://host:3306/dbname \
-e DB_USER=root \
-e DB_PASS=secret \
-p 8080:8080 my-service
π What the Interviewer Expectes You to Say
Hereβs a strong answer:
βWe build the Docker image with just the microservice code and dependencies. The database is not embedded in the image β itβs managed separately and connected via configuration, usually passed through environment variables. This makes the image portable and the architecture more scalable.β
If youβre using Docker Compose, you can define separate services:
version: '3'
services:
my-service:
image: my-service
ports:
- 8080:8080
environment:
- DB_URL=jdbc:mysql://db:3306/mydb
- DB_USER=root
- DB_PASS=secret
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: mydb
β Clarifying the Confusion: Docker Image vs Docker Compose
πΈ What you should say:
in docker-compose.yml, you can define both the application and the database as separate services. β
But what the interviewer likely ask is:
ββWhy are you configuring the database inside the Docker image?β
π§ Core Concept
| Concept | Contains | Purpose |
|---|---|---|
| π³ Docker Image | Your microservice code only | Used to build a container |
| π§© Docker Compose | Definitions for multiple containers (app, DB, cache, etc.) | Used to run multi-container systems |
So yes:
- β Wrong: Putting the DB inside the Docker image.
- β
Right: Referencing the DB in
docker-compose.ymlas a separate service.
β What You Should say
βWe donβt include the database in the Docker image. The image only contains the microservice code. If needed, we use Docker Compose to spin up both the service and the DB as separate containers, where the app connects to the DB via environment variables.β
β
Correct docker-compose.yml Example
Hereβs what a proper setup looks like:
version: '3.8'
services:
app:
image: my-java-service
ports:
- "8080:8080"
environment:
- DB_URL=jdbc:mysql://db:3306/mydb
- DB_USER=root
- DB_PASS=secret
depends_on:
- db
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: mydb
ports:
- "3306:3306"
πΉ Notes:
appanddbare separate containers.- They communicate using internal Docker network (
db:3306). - The app image has no DB inside it β it only knows how to connect to one.
β Summary for Interview
βThe Docker image contains only the microservice. We donβt bake the DB into the image. If we want to run both the app and DB locally, we use Docker Compose to spin them up as separate services. The app connects to the DB using environment variables or config files, ensuring separation of concerns and scalability.β
What Does Docker Compose Do?
πΉ Docker Compose is a tool to define and manage multi-container Docker applications.
Instead of running each container manually one by one, Compose lets you describe all your services (app, database, cache, etc.) in a single YAML file (docker-compose.yml), and then:
- Start them all together with one command.
- Handle networking between containers automatically.
- Manage dependencies (e.g., start DB before your app).
- Make configuration reusable and easy to share.
In the example we discussed:
services:
app:
image: my-java-service
ports:
- "8080:8080"
environment:
- DB_URL=jdbc:mysql://db:3306/mydb
- DB_USER=root
- DB_PASS=secret
depends_on:
- db
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: mydb
ports:
- "3306:3306"
app: This is your Java microservice container.db: This is the MySQL database container.
What happens when you run:
docker-compose up
- Docker Compose creates a network where both containers live.
- It starts the
dbcontainer first (becauseappdepends ondb). - It starts the
appcontainer next. - Inside the
appcontainer, your microservice uses the environment variableDB_URL=jdbc:mysql://db:3306/mydbto connect to thedbcontainer by its service namedb(Docker Compose automatically sets up DNS for that). - Both containers run simultaneously and can talk to each other via the Docker network.
- Ports like
8080and3306are exposed to your host machine, so you can access your microservice and database outside Docker if needed.
Benefits of Docker Compose:
- Easy multi-container setup β no need to manually run each container.
- Consistent environment for development, testing, CI/CD pipelines.
- Service dependency management with
depends_on. - Port mapping and environment variable injection.
- Reusable configuration β share your
docker-compose.ymlwith teammates.
TL;DR
Docker Compose lets you spin up your entire app environment (Java service + DB) in one command, managing networking and dependencies, making local development and testing simple and consistent.
What Does Docker Compose Do?
πΉ Docker Compose is a tool to define and manage multi-container Docker applications.
Instead of running each container manually one by one, Compose lets you describe all your services (app, database, cache, etc.) in a single YAML file (docker-compose.yml), and then:
- Start them all together with one command.
- Handle networking between containers automatically.
- Manage dependencies (e.g., start DB before your app).
- Make configuration reusable and easy to share.
In the example we discussed:
services:
app:
image: my-java-service
ports:
- "8080:8080"
environment:
- DB_URL=jdbc:mysql://db:3306/mydb
- DB_USER=root
- DB_PASS=secret
depends_on:
- db
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: mydb
ports:
- "3306:3306"
app: This is your Java microservice container.db: This is the MySQL database container.
What happens when you run:
docker-compose up
- Docker Compose creates a network where both containers live.
- It starts the
dbcontainer first (becauseappdepends ondb). - It starts the
appcontainer next. - Inside the
appcontainer, your microservice uses the environment variableDB_URL=jdbc:mysql://db:3306/mydbto connect to thedbcontainer by its service namedb(Docker Compose automatically sets up DNS for that). - Both containers run simultaneously and can talk to each other via the Docker network.
- Ports like
8080and3306are exposed to your host machine, so you can access your microservice and database outside Docker if needed.
Benefits of Docker Compose:
- Easy multi-container setup β no need to manually run each container.
- Consistent environment for development, testing, CI/CD pipelines.
- Service dependency management with
depends_on. - Port mapping and environment variable injection.
- Reusable configuration β share your
docker-compose.ymlwith teammates.
TL;DR
Docker Compose lets you spin up your entire app environment (Java service + DB) in one command, managing networking and dependencies, making local development and testing simple and consistent.