eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Overview

In this previous article, we covered a theoretical introduction about Kubernetes.

In this tutorial, we’ll discuss how to deploy a Spring Boot application on a local Kubernetes environment, also known as Minikube.

As part of this article, we’ll:

  • Install Minikube on our local machine
  • Develop an example application consisting of two Spring Boot services
  • Set up the application on a one-node cluster using Minikube
  • Deploy the application using config files

2. Installing Minikube

The installation of Minikube basically consists of three steps: installing a Hypervisor (like VirtualBox), the CLI kubectl, as well as Minikube itself.

The official documentation provides detailed instructions for each of the steps, and for all popular operating systems.

After completing the installation, we can start Minikube, set VirtualBox as Hypervisor, and configure kubectl to talk to the cluster called minikube:

$> minikube start
$> minikube config set vm-driver virtualbox
$> kubectl config use-context minikube

After that, we can verify that kubectl communicates correctly with our cluster:

$> kubectl cluster-info

The output should look like this:

Kubernetes master is running at https://192.168.99.100:8443
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

At this stage, we’ll keep the IP in the response close (192.168.99.100 in our case). We’ll later refer to that as NodeIP, which is needed to call resources from outside of the cluster, e. g. from our browser.

Finally, we can inspect the state of our cluster:

$> minikube dashboard

This command opens a site in our default browser, which provides an extensive overview about the state of our cluster.

4. Demo Application

As our cluster is now running and ready for deployment, we need a demo application.

For this purpose, we’ll create a simple “Hello world” application, consisting of two Spring Boot services, which we’ll call frontend and backend.

The backend provides one REST endpoint on port 8080, returning a String containing its hostname. The frontend is available on port 8081, it will simply call the backend endpoint and return its response.

After that, we have to build a Docker image from each app. All files necessary for that are also available on GitHub.

For detailed instructions how to build Docker images, have a look at Dockerizing a Spring Boot Application.

We have to make sure here that we trigger the build process on the Docker host of the Minikube cluster, otherwise, Minikube won’t find the images later during deployment. Furthermore, the workspace on our host must be mounted into the Minikube VM:

$> minikube ssh
$> cd /c/workspace/tutorials/spring-cloud/spring-cloud-kubernetes/demo-backend
$> docker build --file=Dockerfile \
  --tag=demo-backend:latest --rm=true .

After that, we can logout from the Minikube VM, all further steps will be executed on our host using kubectl and minikube command-line tools.

5. Simple Deployment Using Imperative Commands

In a first step, we’ll create a Deployment for our demo-backend app, consisting of only one Pod. Based on that, we’ll discuss some commands so we can verify the Deployment, inspect logs, and clean it up at the end.

5.1. Creating the Deployment

We’ll use kubectl, passing all required commands as arguments:

$> kubectl run demo-backend --image=demo-backend:latest \
  --port=8080 --image-pull-policy Never

As we can see, we create a Deployment called demo-backend, which is instantiated from an image also called demo-backend, with version latest.

With –port, we specify, that the Deployment opens port 8080 for its Pods (as our demo-backend app listens to port 8080).

The flag –image-pull-policy Never ensures, that Minikube doesn’t try to pull the image from a registry, but takes it from the local Docker host instead.

5.2. Verifying the Deployment

Now, we can check whether the deployment was successful:

$> kubectl get deployments

The output looks like this:

NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
demo-backend   1         1         1            1           19s

If we want to have a look at the application logs, we need the Pod ID first:

$> kubectl get pods
$> kubectl logs <pod id>

5.3. Creating a Service for the Deployment

To make the REST endpoint of our backend app available, we need to create a Service:

$> kubectl expose deployment demo-backend --type=NodePort

–type=NodePort makes the Service available from outside of the cluster. It will be available at <NodeIP>:<NodePort>, i. e. the service maps any request incoming at <NodePort> to port 8080 of its assigned Pods.

We use the expose command, so NodePort will be set by the cluster automatically (this is a technical limitation), the default range is 30000-32767. To get a port of our choice, we can use a configuration file, as we’ll see in the next section.

We can verify that the service was created successfully:

$> kubectl get services

The output looks like this:

NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
demo-backend   NodePort    10.106.11.133   <none>        8080:30117/TCP   11m

As we can see, we have one Service called demo-backend, of type NodePort, which is available at the cluster-internal IP 10.106.11.133.

We have to take a closer look at column PORT(S): as port 8080 was defined in the Deployment, the Service forwards traffic to this port. However, if we want to call the demo-backend from our browser, we have to use port 30117, which is reachable from outside of the cluster.

5.4. Calling the Service

Now, we can call our backend service for the first time:

$> minikube service demo-backend

This command will start our default browser, opening <NodeIP>:<NodePort>. In our example, that would be http://192.168.99.100:30117.

5.5. Cleaning up Service and Deployment

Afterward, we can remove Service and Deployment:

$> kubectl delete service demo-backend
$> kubectl delete deployment demo-backend

6. Complex Deployment Using Configuration Files

For more complex setups, configuration files are a better choice, instead of passing all parameters via command line arguments.

Configurations files are a great way of documenting our deployment, and they can be versioned controlled.

6.1. Service Definition for Our Backend App

Let’s redefine our service for the backend using a config file:

kind: Service
apiVersion: v1
metadata:
  name: demo-backend
spec:
  selector:
    app: demo-backend
  ports:
  - protocol: TCP
    port: 8080
  type: ClusterIP

We create a Service named demo-backend, indicated by the metadata: name field.

It targets TCP port 8080 on any Pod with the app=demo-backend label.

Finally, type: ClusterIP indicates that it is only available from inside of the cluster (as we want to call the endpoint from our demo-frontend app this time, but not directly from a browser anymore, as in the previous example).

6.2. Deployment Definition for Backend App

Next, we can define the actual Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-backend
spec:
  selector:
      matchLabels:
        app: demo-backend
  replicas: 3
  template:
    metadata:
      labels:
        app: demo-backend
    spec:
      containers:
        - name: demo-backend
          image: demo-backend:latest
          imagePullPolicy: Never
          ports:
            - containerPort: 8080

We create a Deployment named demo-backend, indicated by the metadata: name field.

The spec: selector field defines how the Deployment finds which Pods to manage. In this case, we merely select on one label defined in the Pod template (app: demo-backend).

We want to have three replicated Pods, which we indicate by the replicas field.

The template field defines the actual Pod:

  • The Pods are labeled as app: demo-backend
  • The template: spec field indicates that each Pod replication runs one container, demo-backend, with version latest
  • The Pods open port 8080

6.3. Deployment of the Backend App

We can now trigger the deployment:

$> kubectl create -f backend-deployment.yaml

Let’s verify that the deployment was successful:

$> kubectl get deployments

The output looks like this:

NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
demo-backend   3         3         3            3           25s

We can also check whether the Service is available:

$> kubectl get services

The output looks like this:

NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
demo-backend    ClusterIP   10.102.17.114   <none>        8080/TCP         30s

As we can see, the Service is of type ClusterIP, and it doesn’t provide an external port in the range 30000-32767, different from our previous example in section 5.

6.4. Deployment and Service Definition for Our Frontend App

After that, we can define Service and Deployment for the frontend:

kind: Service
apiVersion: v1
metadata:
  name: demo-frontend
spec:
  selector:
    app: demo-frontend
  ports:
  - protocol: TCP
    port: 8081
    nodePort: 30001
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-frontend
spec:
  selector:
      matchLabels:
        app: demo-frontend
  replicas: 3
  template:
    metadata:
      labels:
        app: demo-frontend
    spec:
      containers:
        - name: demo-frontend
          image: demo-frontend:latest
          imagePullPolicy: Never
          ports:
            - containerPort: 8081

Both frontend and backend are almost identical, the only difference between backend and frontend is the spec of the Service:

For the frontend, we define the type as NodePort (as we want to make the frontend available to outside of the cluster). The backend only has to be reachable from within the cluster, therefore, the type was ClusterIP.

As said before, we also specify NodePort manually, using the nodePort field.

6.5. Deployment of the Frontend App

We can now trigger this deployment the same way:

$> kubectl create -f frontend-deployment.yaml

Let’s quickly verify that the deployment was successful and the Service is available:

$> kubectl get deployments
$> kubectl get services

After that, we can finally call the REST endpoint of the frontend application:

$> minikube service demo-frontend

This command will again start our default browser, opening <NodeIP>:<NodePort>, which is http://192.168.99.100:30001 for this example.

6.6. Cleaning up Services and Deployments

In the end, we can clean up by removing Services and Deployments:

$> kubectl delete service demo-frontend
$> kubectl delete deployment demo-frontend
$> kubectl delete service demo-backend
$> kubectl delete deployment demo-backend

7. Conclusion

In this article, we had a quick look at how to deploy a Spring Boot “Hello world” app on a local Kubernetes cluster using Minikube.

We discussed in detail, how to:

  • Install Minikube on our local machine
  • Develop and build an example consisting of two Spring Boot apps
  • Deploy the services on a one-node cluster, using imperative commands with kubectl as well as configuration files
The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)