Containerizing and Deploying a Java Application with Docker
A typical Java Docker workflow is:
- Build the Java application
- Package it as a JAR
- Create a Docker image
- Run the container locally
- Push the image to a registry
- 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-jreuses a Java 25 runtime imageWORKDIR /appsets the working directory inside the containerCOPY target/*.jar app.jarcopies your packaged JAR into the imageEXPOSE 8080documents that the app listens on port8080ENTRYPOINTstarts 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.
