Skip to the content.

Spring Boot + Kubernetes + MySQL

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.


🎯 Problem Statement

Running a Spring Boot application locally with MySQL is straightforward, but deploying the same application to Kubernetes introduces several architectural concerns:

This project demonstrates a practical solution using:

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.


πŸ—οΈ High-Level Architecture

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

Request Flow

Client
  β”‚
  β–Ό
Kubernetes Service
  β”‚
  β–Ό
Spring Boot Pod
  β”‚
  β”‚ JDBC
  β–Ό
MySQL Kubernetes Service
  β”‚
  β–Ό
MySQL Pod
  β”‚
  β–Ό
PersistentVolumeClaim

Key Design Principles

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.


πŸ”„ Deployment Architecture

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 Stack

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

πŸ“‹ Prerequisites

Install the following tools before starting:

Verify the installation:

java -version
gradle -version
docker --version
kubectl version --client

Make sure Kubernetes is running:

kubectl cluster-info

You can use Docker Desktop’s built-in Kubernetes environment for local development.


πŸš€ Getting Started

1. Clone the repository

git clone https://github.com/ashutoshsahoo/spring-boot-kubernetes-mysql.git

cd spring-boot-kubernetes-mysql

2. Create Kubernetes Secrets

Create the required database credentials:

kubectl apply -f deployment/secrets.yaml

Verify:

kubectl get secrets

3. Deploy MySQL

Deploy MySQL and its persistent storage:

kubectl apply -f deployment/mysql-deployment.yaml

Check the resources:

kubectl get pods
kubectl get svc
kubectl get pvc

Wait until the MySQL pod reaches Running state:

kubectl get pods -w

4. Build the Spring Boot Application

Build the application using Gradle:

gradle clean build -i --stacktrace

The generated JAR will be available under:

build/libs/

5. Build the Docker Image

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-mysql

If 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.


6. Deploy Spring Boot to Kubernetes

Deploy the application:

kubectl apply -f deployment/app-k8s.yaml

Check the deployment:

kubectl get deployment

Check the application pod:

kubectl get pods

Check the service:

kubectl get svc

πŸ” Verify the Deployment

A healthy deployment should show:

kubectl get pods

Example:

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-mysql

πŸ§ͺ Test the Application

The 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
  }
]

πŸ”— Kubernetes Resource Flow

The deployment can be visualized as:

                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚      Client       β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚ Kubernetes        β”‚
                    β”‚ Service           β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚ Spring Boot       β”‚
                    β”‚ Deployment        β”‚
                    β”‚                   β”‚
                    β”‚ REST API           β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β”‚ JDBC
                              β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚ MySQL Service     β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚ MySQL Pod         β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚ PersistentVolume  β”‚
                    β”‚ Claim             β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ—‚οΈ Project Structure

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

deployment/

Contains the Kubernetes manifests required to deploy the application and database.

Dockerfile

Defines the container image used to package the Spring Boot application.

build.gradle

Defines application dependencies, build configuration and Gradle plugins.


πŸ’Ύ Persistent Storage

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.


πŸ” Secrets Management

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.


🧹 Cleanup

Delete the Spring Boot deployment:

kubectl delete -f deployment/app-k8s.yaml

Delete MySQL:

kubectl delete -f deployment/mysql-deployment.yaml

Delete the Kubernetes Secret:

kubectl delete -f deployment/secrets.yaml

Verify:

kubectl get pods
kubectl get svc
kubectl get pvc
kubectl get secrets

Depending on the storage configuration and reclaim policy, deleting the deployment may not automatically remove the underlying persistent storage.


πŸ“Š Code Analysis

The project can also be analyzed using SonarQube.

Configure the SonarQube URL and authentication token in:

gradle.properties

Then execute:

gradle clean build sonar --stacktrace

🎯 What This Project Demonstrates

This project provides hands-on experience with:


πŸš€ Possible Production Enhancements

For a production-grade deployment, the architecture can be further enhanced with:


πŸ“š References


⭐ About

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.