A production-style reference implementation demonstrating how to deploy a Spring Boot application with MySQL on Kubernetes, including containerization, Kubernetes deployments, persistent storage, secrets management, and service-to-database connectivity.
The project is designed to demonstrate the fundamentals of running a stateful database and a stateless Spring Boot application in Kubernetes.
Running a Spring Boot application locally with MySQL is straightforward, but deploying the same application to Kubernetes introduces several architectural concerns:
- How should the Spring Boot application be packaged and deployed?
- How should the application communicate with MySQL inside the Kubernetes cluster?
- How can MySQL data survive pod/container restarts?
- How should database credentials be managed without hardcoding them in the application?
- How should Kubernetes manage application and database resources independently?
- How can a stateful component such as MySQL be deployed alongside a stateless application?
This project demonstrates a practical solution using:
- Spring Boot for the REST API
- Docker for application containerization
- Kubernetes for orchestration
- MySQL as the relational database
- PersistentVolumeClaim (PVC) for database persistence
- Kubernetes Secrets for database credentials
The goal is not only to run the application in Kubernetes, but to demonstrate the key architectural patterns required when moving a traditional Spring Boot + database application into a containerized environment.
The following diagram represents the overall deployment architecture:
flowchart TB
Client["Client / REST Client"]
subgraph Kubernetes["Kubernetes Cluster"]
Service["Kubernetes Service<br/>Spring Boot"]
App["Spring Boot Application<br/>Deployment / Pod"]
Secret["Kubernetes Secret<br/>DB Credentials"]
DBService["Kubernetes Service<br/>MySQL"]
MySQL["MySQL<br/>Deployment / Pod"]
PVC["PersistentVolumeClaim<br/>Persistent Storage"]
Service --> App
App --> DBService
DBService --> MySQL
Secret -.-> App
Secret -.-> MySQL
MySQL --> PVC
end
Client --> Service
Client
β
βΌ
Kubernetes Service
β
βΌ
Spring Boot Pod
β
β JDBC
βΌ
MySQL Kubernetes Service
β
βΌ
MySQL Pod
β
βΌ
PersistentVolumeClaim
Stateless application
Spring Boot runs as a Kubernetes workload. Application instances do not own persistent state, allowing Kubernetes to restart or recreate pods independently.
Stateful database
MySQL requires persistent storage. A Kubernetes PersistentVolumeClaim is used so database data is not tied to the
lifecycle of a MySQL pod.
Service discovery
The Spring Boot application connects to MySQL through a Kubernetes Service rather than using a pod IP. This allows Kubernetes to provide stable service discovery even when pods are recreated.
Secrets management
Database credentials are stored in Kubernetes Secrets instead of being embedded directly into the application deployment configuration.
The deployment consists of the following Kubernetes resources:
| Component | Kubernetes Resource | Purpose |
|---|---|---|
| Spring Boot | Deployment | Runs the application |
| Spring Boot | Service | Exposes the application |
| MySQL | Deployment | Runs MySQL |
| MySQL | Service | Provides stable database endpoint |
| Database storage | PersistentVolumeClaim | Persists MySQL data |
| Credentials | Secret | Stores database credentials |
| Technology | Purpose |
|---|---|
| Java 21 LTS | Application runtime |
| Spring Boot | REST API |
| Gradle | Build automation |
| Docker | Containerization |
| Kubernetes | Container orchestration |
| MySQL | Relational database |
| Kubernetes PVC | Persistent database storage |
| Kubernetes Secret | Credential management |
Install the following tools before starting:
- Docker Desktop with Kubernetes enabled
- Kubernetes CLI (
kubectl) - JDK 21 LTS
- Gradle 9.x
Verify the installation:
java -version
gradle -version
docker --version
kubectl version --clientMake sure Kubernetes is running:
kubectl cluster-infoYou can use Docker Desktop's built-in Kubernetes environment for local development.
git clone https://github.com/ashutoshsahoo/spring-boot-kubernetes-mysql.git
cd spring-boot-kubernetes-mysqlCreate the required database credentials:
kubectl apply -f deployment/secrets.yamlVerify:
kubectl get secretsDeploy MySQL and its persistent storage:
kubectl apply -f deployment/mysql-deployment.yamlCheck the resources:
kubectl get pods
kubectl get svc
kubectl get pvcWait until the MySQL pod reaches Running state:
kubectl get pods -wBuild the application using Gradle:
gradle clean build -i --stacktraceThe generated JAR will be available under:
build/libs/
Build the application container:
docker build -t ashutoshsahoo/spring-boot-kubernetes-mysql:<app-version> .For example:
docker build -t ashutoshsahoo/spring-boot-kubernetes-mysql:1.0.0 .Verify the image:
docker images | grep spring-boot-kubernetes-mysqlIf you are using Docker Desktop's Kubernetes environment, the Kubernetes cluster can use images available in the Docker environment without requiring an external container registry.
Deploy the application:
kubectl apply -f deployment/app-k8s.yamlCheck the deployment:
kubectl get deploymentCheck the application pod:
kubectl get podsCheck the service:
kubectl get svcA healthy deployment should show:
kubectl get podsExample:
NAME READY STATUS RESTARTS
mysql-xxxxx 1/1 Running 0
spring-boot-xxxxx 1/1 Running 0
Check application logs:
kubectl logs <spring-boot-pod-name>For example:
kubectl logs deployment/spring-boot-kubernetes-mysqlThe application exposes the following REST endpoint:
GET /api/v1/pets
Test using curl:
curl -X GET \
http://localhost:31371/api/v1/pets \
-H "Accept: application/json" \
-H "Content-Type: application/json"Expected response:
[
{
"name": "Puffball",
"owner": "Diane",
"species": "hamster",
"sex": "f",
"birth": "1999-03-30",
"death": null
}
]The deployment can be visualized as:
βββββββββββββββββββββ
β Client β
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββββββββββββ
β Kubernetes β
β Service β
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββββββββββββ
β Spring Boot β
β Deployment β
β β
β REST API β
βββββββββββ¬ββββββββββ
β
β JDBC
βΌ
βββββββββββββββββββββ
β MySQL Service β
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββββββββββββ
β MySQL Pod β
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββββββββββββ
β PersistentVolume β
β Claim β
βββββββββββββββββββββ
spring-boot-kubernetes-mysql/
β
βββ deployment/
β βββ app-k8s.yaml
β βββ mysql-deployment.yaml
β βββ secrets.yaml
β
βββ src/
β βββ main/
β βββ java/
β βββ resources/
β
βββ Dockerfile
βββ build.gradle
βββ gradle.properties
βββ settings.gradle
βββ README.md
Contains the Kubernetes manifests required to deploy the application and database.
Defines the container image used to package the Spring Boot application.
Defines application dependencies, build configuration and Gradle plugins.
MySQL is a stateful workload and therefore requires persistent storage.
This project uses a Kubernetes PersistentVolumeClaim to decouple database storage from the lifecycle of the MySQL pod.
MySQL Pod
β
βΌ
PersistentVolumeClaim
β
βΌ
Persistent Storage
This means that deleting/recreating the MySQL pod does not necessarily mean losing the database data, provided the underlying persistent volume remains available.
Database credentials are managed through Kubernetes Secrets.
Kubernetes Secret
β
ββββΊ Spring Boot
β
ββββΊ MySQL
This avoids putting database credentials directly into application source code.
For production environments, consider integrating a dedicated secrets-management solution such as HashiCorp Vault or a cloud-native secret manager.
Delete the Spring Boot deployment:
kubectl delete -f deployment/app-k8s.yamlDelete MySQL:
kubectl delete -f deployment/mysql-deployment.yamlDelete the Kubernetes Secret:
kubectl delete -f deployment/secrets.yamlVerify:
kubectl get pods
kubectl get svc
kubectl get pvc
kubectl get secretsDepending on the storage configuration and reclaim policy, deleting the deployment may not automatically remove the underlying persistent storage.
The project can also be analyzed using SonarQube.
Configure the SonarQube URL and authentication token in:
gradle.properties
Then execute:
gradle clean build sonar --stacktraceThis project provides hands-on experience with:
- Spring Boot application deployment on Kubernetes
- Docker containerization
- Kubernetes Deployments
- Kubernetes Services
- Kubernetes Secrets
- PersistentVolumeClaims
- Stateful workloads
- Stateless application deployment
- Kubernetes service discovery
- Spring Boot β MySQL connectivity
- Gradle-based builds
- Container image creation
- Kubernetes-based application lifecycle management
- SonarQube code analysis
For a production-grade deployment, the architecture can be further enhanced with:
- Kubernetes
StatefulSetfor MySQL - MySQL replication / high availability
- Helm charts
- Horizontal Pod Autoscaler (HPA)
- Readiness and liveness probes
- Resource requests and limits
- Ingress / Gateway API
- TLS termination
- External Secrets / Vault
- Prometheus and Grafana monitoring
- OpenTelemetry distributed tracing
- Centralized logging
- CI/CD using GitHub Actions
- Container image vulnerability scanning
- NetworkPolicies
- PodDisruptionBudgets
- Spring Boot
- Kubernetes
- Docker
- MySQL
- Gradle
- SonarQube
This repository is a practical reference implementation for deploying a Spring Boot application with MySQL on Kubernetes and understanding the architectural considerations involved in running both stateless and stateful workloads in a Kubernetes environment.