How to create docker image for spring boot application | Angular Project | Microservices Project

In this post, we will delve into how to create a docker image for spring boot application. Let’s kick off by exploring the essential prerequisites for generating an image through Docker.
By the end of this guide you will have:
- Docker images for each Spring Boot microservice (Eureka server, config server, API gateway, auth, user, hotel and invoice services).
- A production-style image for the Angular frontend, served by Nginx.
- A single
docker-compose.ymlthat starts the entire application, including MySQL and Zipkin, with one command.
Prerequisites
- Java and Maven installed, with the Spring Boot project building successfully on your machine.
- Node.js and npm for the Angular frontend.
- Docker Desktop (on Windows this requires WSL 2, covered below).
- Basic familiarity with the command line.
Before diving into the Docker installation, It’s important to note that Docker has a dependency on WSL (Windows Subsystem Linux).
If WSL is not already installed on your system, follow the steps outlined below before proceeding with the Docker installation. If you have WSL set up, feel free to skip ahead to the Docker installation section.
Step 1: Install WSL 2 (Windows only)
Open Windows PowerShell on your system.
Execute the following command in the PowerShell, which will install WSL2 by default.
C:Userscodespy>wsl --installAfter the command finishes, restart your computer so the new Windows features are enabled.
Step 2: Install Docker Desktop
After successfully installing WSL2, proceed by downloading Docker Desktop here. Once your Docker setup is in place, you’re ready to embark on the next steps with me.
The Sample Project
To illustrate the process of creating a Docker image, let’s employ a sample project—I’ll use the Online Hotel Booking Management System from my previous work.
While you can learn more about this project, it’s important to note that understanding the project specifics isn’t necessary for creating the image. I’ll guide you through every step in this post, covering both the frontend and backend applications.
Starting with the backend, we’ll be utilizing the Spring Tool Suite as the IDE. If you’re unfamiliar with this IDE, you can refer to this video.
Part 1: Create Docker Images for the Spring Boot Microservices
Step 1: Configure the Spring Boot Maven plugin
Now, let’s dive into creating an image for our Spring Boot microservice application. Begin by making a few adjustments to your project.
- Open the
pom.xmlfile and update the build plugin with the following configuration:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>codespy/mmv2-${project.artifactId}:${project.version}</name>
</image>
<pullPolicy>IF_NOT_PRESENT</pullPolicy>
</configuration>
</plugin>
</plugins>
</build>If the build plugin hasn’t been defined, insert the provided code into your pom.xml file. If it’s already present, simply append the configuration from lines 6 to 11.
Step 2: Add a Dockerfile
Subsequently, create a file named Dockerfile in your project’s root folder, next to pom.xml. Docker looks for exactly this name by default (capital D, lowercase everything else, no file extension), so other spellings will only work if you point Docker to the file explicitly.

Add these configurations in DockerFile.
FROM java:8-jdk
EXPOSE 8761
ADD eureka-0.0.1-SNAPSHOT.jar eureka-server.jar
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "eureka-server.jar"]The FROM directive specifies a base image upon which your application image is built. This image includes the Java runtime environment necessary for running Java applications.
In case the base image isn’t present locally, Docker will retrieve it from the Docker Hub.
The port 8761 is designated for the Docker container to listen to your application. The process involves copying the application JAR file and giving it a distinct name.
The EntryPoint is configured to include all the information or commands to execute within the container during runtime.
Step 3: Build the image
Follow the same steps for other microservices. After adding all these configurations, we are ready to build the image. Do right-click on your project and select Run As → Maven build…
Then define goals, something like this:-
spring-boot:build-image -DskipTestsOnce, you define goals, click on run. Make sure docker desktop is running, otherwise, it will not be able to build an image of your application.
It will take some time and once it is done, you will see a build success message on the console.

Buildpacks or Dockerfile? The spring-boot:build-image goal uses Cloud Native Buildpacks: it builds an optimised image from your project and the image name configured in pom.xml, and it does not read your Dockerfile. If you prefer to control the image yourself with the Dockerfile from Step 2, package the JAR with mvn clean package -DskipTests and run docker build from the project folder instead. Both approaches produce an image you can use in Docker Compose; just keep the image names consistent.
mvn clean package -DskipTests
docker build -t codespy/mmv2-eureka:0.0.1-SNAPSHOT .To streamline the image creation process for other applications, follow these identical steps. This procedure ensures the generation of an image for your backend or microservices application.
Part 2: Dockerize the Angular Frontend
The frontend is built differently: Angular compiles to static HTML, CSS and JavaScript files, so the final image only needs a lightweight web server, not Node.js. A multi-stage build does exactly that: the first stage builds the app with Node, and the second copies only the build output into an Nginx image.
Step 1: Create a multi-stage Dockerfile
For the frontend, which employs Angular, the image creation process for the client application involves the following steps:
Create a Dockerfile in the frontend directory. It’s crucial to name the file “Dockerfile.”
Add the specified configurations to this Dockerfile.
### STAGE 1: Build ###
FROM node:20.9.0-alpine AS build
WORKDIR /usr/src/app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
### STAGE 2: Run ###
FROM nginx:1.17.1-alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=build /usr/src/app/dist/hotel-service-app /usr/share/nginx/htmlIn the first stage, you’ll find configurations dedicated to building your project, while the second stage is responsible for crafting the image of your application.
Step 2: Configure Nginx
For this image creation, we leverage the nginx server, which comes with its distinct set of configurations.
To implement this, establish a file named “nginx.conf” within your project’s root folder and incorporate these configurations into the file.
events{}
http {
include /etc/nginx/mime.types;
server {
listen 80;
location / {
add_header "Access-Control-Allow-Origin" "*";
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'X-Requested-With,Accept,Content-Type, Origin';
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;
}
}
}The try_files line is important for Angular: it sends every unknown route back to index.html, so deep links such as /bookings/42 work when the page is refreshed instead of returning a 404.
Step 3: Add a .dockerignore file
While building the docker image, you can also maintain docker ignore file to ignore the scanning for unnecessary files, repository or dependency etc.,

At a minimum, exclude node_modules, dist and .git. This makes builds faster and keeps the image small, because those folders are either rebuilt inside the container or not needed at all.
node_modules
dist
.git
.angular
*.logStep 4: Build the frontend image
Now, we are ready to create an image of client application. Paste this command on the terminal and hit enter.

After build completion, you will see logs on your terminal.

Part 3: Run Everything with Docker Compose
Now, we are ready with docker image for both frontend and backend application. We will use docker compose to run both frontend and backend application.
Docker compose is one of the best tool to run multiple containers at once. For that, we need to create docker-compose.yml and add all the image information.
version: "3.8"
services:
zipkin-server:
image: openzipkin/zipkin:2.23
mem_limit: 300m
ports:
- "9411:9411"
networks:
- hotel-microservices-network
mysqldb:
image: mysql:latest
networks:
- hotel-microservices-network
environment:
MYSQL_ROOT_PASSWORD: 123
MYSQL_PASSWORD: 123
ports:
- "3307:3306"
volumes:
- ./init:/docker-entrypoint-initdb.d
eureka-server:
image: codespy/mmv2-eureka:0.0.1-SNAPSHOT
mem_limit: 700m
ports:
- "8761:8761"
networks:
- hotel-microservices-network
hotel-config-server:
image: codespy/mmv2-config:0.0.1-SNAPSHOT
mem_limit: 700m
ports:
- "9700:9700"
networks:
- hotel-microservices-network
depends_on:
- eureka-server
environment:
EUREKA.CLIENT.SERVICEURL.DEFAULTZONE: http://eureka-server:8761/eureka
SPRING.ZIPKIN.BASEURL: http://zipkin-server:9411/
api-gateway:
image: codespy/mmv2-api-gateway:0.0.1-SNAPSHOT
environment:
EUREKA.CLIENT.SERVICEURL.DEFAULTZONE: http://eureka-server:8761/eureka
SPRING.ZIPKIN.BASEURL: http://zipkin-server:9411/
ports:
- "9500:9500"
depends_on:
- eureka-server
- hotel-config-server
networks:
- hotel-microservices-network
auth-service:
image: codespy/mmv2-auth:0.0.1-SNAPSHOT
environment:
EUREKA.CLIENT.SERVICEURL.DEFAULTZONE: http://eureka-server:8761/eureka
SPRING.ZIPKIN.BASEURL: http://zipkin-server:9411/
ports:
- "9508:9508"
networks:
- hotel-microservices-network
depends_on:
- eureka-server
- hotel-config-server
user-service:
image: codespy/mmv2-user-service:0.0.1-SNAPSHOT
environment:
SPRING.DATASOUCE.URL: jdbc:mysql://mysqldb:3306/user_service?useSSL=false
EUREKA.CLIENT.SERVICEURL.DEFAULTZONE: http://eureka-server:8761/eureka
SPRING.ZIPKIN.BASEURL: http://zipkin-server:9411/
ports:
- "9501:9501"
networks:
- hotel-microservices-network
depends_on:
- eureka-server
- hotel-config-server
- mysqldb
invoice-service:
image: codespy/mmv2-invoice-service:0.0.1-SNAPSHOT
environment:
SPRING.DATASOUCE.URL: jdbc:mysql://mysqldb:3306/invoice_service?useSSL=false
EUREKA.CLIENT.SERVICEURL.DEFAULTZONE: http://eureka-server:8761/eureka
SPRING.ZIPKIN.BASEURL: http://zipkin-server:9411/
ports:
- "9503:9503"
networks:
- hotel-microservices-network
depends_on:
- eureka-server
- hotel-config-server
- mysqldb
hotel-service:
image: codespy/mmv2-hotel-service:0.0.1-SNAPSHOT
environment:
SPRING.DATASOUCE.URL: jdbc:mysql://mysqldb:3306/hotel_service?useSSL=false
EUREKA.CLIENT.SERVICEURL.DEFAULTZONE: http://eureka-server:8761/eureka
SPRING.ZIPKIN.BASEURL: http://zipkin-server:9411/
ports:
- "9502:9502"
networks:
- hotel-microservices-network
depends_on:
- eureka-server
- hotel-config-server
- mysqldb
angular:
image: 0e40c8a6baff
ports:
- "4200:80"
networks:
- hotel-microservices-angular
volumes:
db_data:
networks:
hotel-microservices-network:
hotel-microservices-angular: After adding all these configurations in docker-compose file, just go the directory, where you have created this file, open terminal and execute this command.
docker-compose -f docker-compose.yml up -dVerify the running containers
After running this command, it will run all the images as docker container.

You can also check docker desktop to know the status of running containers.

You can also check the services from the terminal with docker compose ps, follow a service’s logs with docker compose logs -f user-service, and stop everything with docker compose down.
Troubleshooting Common Errors
- “Cannot connect to the Docker daemon” – Docker Desktop is not running. Start it and wait until it shows as running before building.
- “Port is already allocated” – another process or container is using that port. Stop it, or change the host side of the mapping (for example
"3308:3306"). - Services cannot reach each other – inside Docker, services talk to each other by service name (
mysqldb,eureka-server), neverlocalhost. They must also be on the same Docker network. - Database connection errors at startup –
depends_ononly waits for the MySQL container to start, not for MySQL to be ready. Add a healthcheck or retry logic, and check that the datasource variable name is spelled exactlySPRING_DATASOURCE_URL. - Angular page shows 404 on refresh – make sure the
try_files ... /index.htmlrule is innginx.confand that the file was copied into the image. - Containers exit with out-of-memory errors – raise the
mem_limitfor that service or the memory allocated to Docker Desktop.
Best Practices Before Going to Production
- Use maintained base images – the old
java:8-jdkimage is deprecated. Use an official, maintained image such aseclipse-temurin(for exampleeclipse-temurin:8-jreor a newer Java version), and a currentnginx:stable-alpineimage for the frontend. - Pin image versions – avoid
latest(for examplemysql:latest) so a new upstream release cannot break your setup unexpectedly. - Keep secrets out of the compose file – move database passwords into an
.envfile or Docker secrets instead of committing them. - Tag your own images properly – refer to the Angular image by a name and version (for example
codespy/hotel-frontend:1.0) rather than an image ID. - Use Docker Compose v2 – current Docker Desktop ships
docker compose(with a space), and the top-levelversionkey in the compose file is no longer required. - Add health checks – so Docker and your orchestrator know when each service is actually ready, not just started.
Conclusion
You now have a fully containerised application: an image for every Spring Boot microservice, an Nginx-served Angular frontend, and a single Docker Compose file that brings the whole system up with one command. The same pattern works for almost any Spring Boot and Angular project, and it is the natural first step before moving to Kubernetes or a cloud container service.
If you are still facing an issue, leave a comment below or contact us and we will get back to you as soon as possible.


