I have containerized a pre-existing Spring Boot + Thymeleaf Java application along with a MySQL database, using Docker Compose to create a reliable, reproducible multi-container environment that handles service orchestration, networking, and data persistence.
-
Multi-Stage Dockerfile
- Build stage: compile the application with Maven and OpenJDK (
maven:3.8.3-openjdk-17) to produce an executable JAR. - Runtime stage: start from a minimal Alpine-based OpenJDK 17 image (
bellsoft/liberica-openjdk-alpine:17), copy in only the packaged JAR, and launch it—keeping the final image lean.
- Build stage: compile the application with Maven and OpenJDK (
-
Docker Compose v3.8
Orchestrate two services—java_app(Spring Boot + Thymeleaf) andmysql_db—on an isolated bridge network, mount a named volume for persistent MySQL data, and use health checks to ensure proper startup order.
flowchart LR
BROWSER["Browser (Thymeleaf UI)"]
APP["java_app (Spring Boot)"]
DB["mysql_db (MySQL Container)"]
VOLUME["mysql-data (Docker Volume)"]
BROWSER -->|HTTP| APP
APP -->|JDBC| DB
DB -->|Persists Data| VOLUME
subgraph "Docker Network"
direction LR
APP
DB
end
-
Clone the repository
git clone https://github.com/DevDetroja10/Dockerized-3tier-java-app.git
-
Build and start all services
docker compose up
-
Access the application
Open your browser and navigate to:http://localhost:8080
⚠️ Note: The application may take a few seconds to fully start after the database initializes. If you encounter a connection error, wait briefly and refresh the page.
- Stop all services
docker compose down
| Variable | Description |
|---|---|
SPRING_DATASOURCE_URL |
JDBC URL for connecting to the MySQL container |
SPRING_DATASOURCE_USERNAME |
Database username used by the Spring Boot app |
SPRING_DATASOURCE_PASSWORD |
Database password used by the Spring Boot app |
MYSQL_ROOT_PASSWORD |
Root password for the MySQL container |
MYSQL_DATABASE |
Name of the initial MySQL database to be created |
- Multi-stage build: Keeps final image small by discarding Maven and source code.
- Isolated network: Ensures secure inter-service communication.
- Healthchecks + depends_on: Guarantees the Java app only starts after MySQL is healthy.
- Named volume for MySQL: Preserves data across container restarts.