One of the most frustrating experiences in software development is the “it works on my machine” syndrome. I’ve spent countless hours debugging environment discrepancies between my local Mac and a Linux production server. The solution, as most of you know, is containerization. In this dockerize spring boot application tutorial, I’m going to walk you through the exact process I use to wrap my Java services into lightweight, portable Docker images.

Whether you are preparing for a deployment of Spring Boot on AWS or just want to standardize your local development environment, Docker is the industry standard. Let’s dive in.

Prerequisites

Step 1: Build Your Application Artifact

Before we can containerize the app, we need a deployable JAR file. Docker doesn’t run your source code directly; it runs the compiled bytecode. Run the following command in your project root:

./mvnw clean package -DskipTests

I usually skip tests during the image build phase to speed things up, assuming my CI/CD pipeline has already validated the code. This will generate a .jar file in the target/ folder.

Step 2: Creating the Dockerfile

The Dockerfile is the blueprint for your image. While you could use a simple one-liner, I always recommend multi-stage builds. This allows you to compile the code inside a container and then copy only the final JAR into a smaller, production-ready image, reducing the attack surface and image size.

Create a file named Dockerfile in your project root and paste the following:

# Stage 1: Build
FROM maven:3.9.6-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests

# Stage 2: Run
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

As shown in the image below, using the alpine version of the JRE significantly cuts down the final image size from ~300MB to under 150MB.

Comparison of Docker image sizes between standard OpenJDK and Alpine JRE images
Comparison of Docker image sizes between standard OpenJDK and Alpine JRE images

Step 3: Building and Running the Container

Now that we have our Dockerfile, it’s time to build the image. I use a descriptive tag to keep track of versions.

docker build -t spring-boot-app:v1 .

Once the build finishes, run your container and map the internal port 8080 to your machine’s port 8080:

docker run -p 8080:8080 spring-boot-app:v1

You can now access your application at http://localhost:8080. If you’re planning to scale this across multiple services, you might want to look into a Spring Cloud vs Kubernetes comparison to decide how to manage these containers at scale.

Pro Tips for Production

1. Optimize Layer Caching

In my experience, the slowest part of the build is downloading Maven dependencies. To fix this, copy only the pom.xml first and run mvn dependency:go-offline. This way, Docker caches your dependencies, and they are only re-downloaded when your pom.xml changes, not every time you edit a Java file.

2. Use Non-Root Users

By default, Docker runs as root. In a production environment, this is a security risk. Add a user to your Dockerfile:

RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring

3. Environmental Variables

Never hardcode database credentials in your application.properties. Use environment variables and pass them during runtime:

docker run -e SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/mydb spring-boot-app:v1

Troubleshooting Common Issues

Issue Cause Solution
Could not find or load main class Wrong JAR path in COPY command Check target/ folder for the exact JAR name.
Container exits immediately Spring Boot failed to start (likely DB connection) Check logs using docker logs [container_id].
Slow build times Dependencies re-downloading every time Implement the layer caching tip mentioned above.

What’s Next?

Now that you can containerize a single app, the next step is orchestration. I recommend learning Docker Compose to manage your Spring Boot app alongside a PostgreSQL or Redis container. Once you’ve mastered that, you can explore the cloud deployment strategies we’ve covered in our other guides.