How do I containerize and deploy a Java application with Docker?

Containerizing and Deploying a Java Application with Docker

A typical Java Docker workflow is:

  1. Build the Java application
  2. Package it as a JAR
  3. Create a Docker image
  4. Run the container locally
  5. Push the image to a registry
  6. Deploy it to a server or cloud platform

1. Build Your Java Application

If your project uses Maven, build it with:

mvn clean package

This usually creates a JAR file under:

target/

For example:

target/my-application.jar

If this is a Spring Boot application, the generated JAR is often executable and can be run with:

java -jar target/my-application.jar

2. Create a Dockerfile

Create a file named Dockerfile in the root of your project.

Simple Dockerfile

FROM eclipse-temurin:25-jre

WORKDIR /app

COPY target/*.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

What this does

  • FROM eclipse-temurin:25-jre uses a Java 25 runtime image
  • WORKDIR /app sets the working directory inside the container
  • COPY target/*.jar app.jar copies your packaged JAR into the image
  • EXPOSE 8080 documents that the app listens on port 8080
  • ENTRYPOINT starts the Java application

3. Add a .dockerignore File

Create a .dockerignore file to avoid copying unnecessary files into the Docker build context:

.git
.idea
*.iml
target
.DS_Store

If your Dockerfile copies from target/*.jar, you can still ignore most build artifacts carefully, but do not ignore the final JAR unless you use a multi-stage build.

A safer option is:

.git
.idea
*.iml
.DS_Store

4. Build the Docker Image

After running mvn clean package, build the image:

docker build -t my-java-app:1.0 .

You can also tag it as latest:

docker build -t my-java-app:latest .

5. Run the Container Locally

Run the container with:

docker run --name my-java-app -p 8080:8080 my-java-app:1.0

Then open:

http://localhost:8080

If your application uses a different internal port, change the second port value:

docker run -p 8080:9090 my-java-app:1.0

This maps:

host port 8080 -> container port 9090

6. Use Environment Variables

Most real applications need configuration such as database URLs, credentials, profiles, or API keys.

Example:

docker run \
  --name my-java-app \
  -p 8080:8080 \
  -e SPRING_PROFILES_ACTIVE=prod \
  -e DB_URL=jdbc:postgresql://db:5432/appdb \
  my-java-app:1.0

For Spring Boot, common environment variables include:

SPRING_PROFILES_ACTIVE=prod
SERVER_PORT=8080
SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/appdb
SPRING_DATASOURCE_USERNAME=appuser
SPRING_DATASOURCE_PASSWORD=secret

7. Multi-Stage Dockerfile

A better production approach is to build the application inside Docker.

FROM maven:3.9-eclipse-temurin-25 AS build

WORKDIR /app

COPY pom.xml .
COPY src ./src

RUN mvn clean package -DskipTests

FROM eclipse-temurin:25-jre

WORKDIR /app

COPY --from=build /app/target/*.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

This gives you:

  • Reproducible builds
  • No need to install Maven locally
  • A smaller final image because Maven is not included in the runtime image

8. Docker Compose Example

If your Java app needs a database, use Docker Compose.

Create docker-compose.yml:

services:
  app:
    build: .
    container_name: my-java-app
    ports:
      - "8080:8080"
    environment:
      SPRING_PROFILES_ACTIVE: docker
      SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/appdb
      SPRING_DATASOURCE_USERNAME: appuser
      SPRING_DATASOURCE_PASSWORD: secret
    depends_on:
      - db

  db:
    image: postgres:17
    container_name: app-postgres
    environment:
      POSTGRES_DB: appdb
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: secret
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:

Run it with:

docker compose up --build

Stop it with:

docker compose down

Remove volumes too:

docker compose down -v

9. Push the Image to a Registry

Tag the image for Docker Hub:

docker tag my-java-app:1.0 your-dockerhub-username/my-java-app:1.0

Log in:

docker login

Push:

docker push your-dockerhub-username/my-java-app:1.0

For GitHub Container Registry:

docker tag my-java-app:1.0 ghcr.io/your-github-username/my-java-app:1.0
docker push ghcr.io/your-github-username/my-java-app:1.0

10. Deploy on a Server

On your server:

docker pull your-dockerhub-username/my-java-app:1.0

Run it:

docker run -d \
  --name my-java-app \
  --restart unless-stopped \
  -p 80:8080 \
  -e SPRING_PROFILES_ACTIVE=prod \
  your-dockerhub-username/my-java-app:1.0

Now your app is available on:

http://your-server-ip

11. Production-Friendly Dockerfile

For a more production-ready Java container, add memory options and a non-root user.

FROM eclipse-temurin:25-jre

WORKDIR /app

RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser

COPY target/*.jar app.jar

RUN chown appuser:appgroup app.jar

USER appuser

EXPOSE 8080

ENV JAVA_OPTS=""

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

Run with JVM options:

docker run \
  -p 8080:8080 \
  -e JAVA_OPTS="-Xms256m -Xmx512m" \
  my-java-app:1.0

12. Common Commands

List images

docker images

List running containers

docker ps

List all containers

docker ps -a

View logs

docker logs my-java-app

Follow logs:

docker logs -f my-java-app

Stop container

docker stop my-java-app

Remove container

docker rm my-java-app

Remove image

docker rmi my-java-app:1.0

Open shell in container

docker exec -it my-java-app sh

Recommended Minimal Setup

For most Java web applications, start with these two files.

Dockerfile

FROM eclipse-temurin:25-jre

WORKDIR /app

COPY target/*.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

.dockerignore

.git
.idea
*.iml
.DS_Store

Then run:

mvn clean package
docker build -t my-java-app:1.0 .
docker run -p 8080:8080 my-java-app:1.0

That is the basic end-to-end flow for containerizing and deploying a Java application with Docker.