Skip to content
Projects · 12 min read

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

docker-image-spring-boot

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.yml that 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)

  1. Open Windows PowerShell on your system.

  2. Execute the following command in the PowerShell, which will install WSL2 by default.

C:Userscodespy>wsl --install

After 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.xml file 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.

Dockerfile placed in the root folder of the Spring Boot project

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 -DskipTests

Once, 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.

Maven build success output for the Spring Boot project

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:

  1. Create a Dockerfile in the frontend directory. It’s crucial to name the file “Dockerfile.”

  2. 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/html

In 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.,

.dockerignore file for the Angular project

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
*.log

Step 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.

Docker build command for the Angular frontend image

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

Building the Angular Docker image from the 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 -d

Verify the running containers

After running this command, it will run all the images as docker container.

Containers started by Docker Compose shown in the terminal

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

Running containers listed in Docker Desktop

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), never localhost. They must also be on the same Docker network.
  • Database connection errors at startup – depends_on only 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 exactly SPRING_DATASOURCE_URL.
  • Angular page shows 404 on refresh – make sure the try_files ... /index.html rule is in nginx.conf and that the file was copied into the image.
  • Containers exit with out-of-memory errors – raise the mem_limit for that service or the memory allocated to Docker Desktop.

Best Practices Before Going to Production

  • Use maintained base images – the old java:8-jdk image is deprecated. Use an official, maintained image such as eclipse-temurin (for example eclipse-temurin:8-jre or a newer Java version), and a current nginx:stable-alpine image for the frontend.
  • Pin image versions – avoid latest (for example mysql:latest) so a new upstream release cannot break your setup unexpectedly.
  • Keep secrets out of the compose file – move database passwords into an .env file 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-level version key 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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Newsletter

Get the next guide before anyone else.

One email a week with new playbooks and tutorials. No spam, unsubscribe anytime.