Modernizing Your Stack: Migrating a Spring Boot App to Quarkus Step-by-Step
A hands-on, real-world walkthrough to help you modernize your Spring Boot application: Covering dependencies, JPA, REST, configuration, and testing.
You don’t migrate an app just because it’s trendy. You do it because your current stack slows you down, costs more to run, or can’t keep up with modern expectations. Quarkus offers a faster, leaner, cloud-native path for Java developers. And it’s built to make migration approachable, especially if you’re coming from Spring Boot.
In this article, we’ll guide you through the practical steps of modernizing a simple Spring Boot application using Quarkus. If your app uses Spring Web, Spring Data JPA, external configuration, and standard JUnit tests, you’ll find this migration path both familiar and refreshing.
An example Project We’re Migrating
We’re starting with a basic Spring Boot app that:
Exposes a
/api/quotes
REST endpointStores motivational quotes in a PostgreSQL database
Uses
@RestController
,@Repository
, and@Entity
Loads config from
application.properties
Uses
@SpringBootTest
for integration tests
This type of app is perfect for learning Quarkus, since it touches the most common parts of real-world applications.
Creating the Quarkus Project
Before porting code, let’s scaffold a Quarkus project to play around with.
mvn io.quarkus:quarkus-maven-plugin:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=quarkus-quotes \
-DclassName="org.acme.quotes.QuoteResource" \
-Dpath="/api/quotes" \
-Dextensions="rest-jackson, hibernate-orm-panache, jdbc-postgresql"
This sets up a Maven project with REST, JPA (Panache), and PostgreSQL support. The extension model in Quarkus replaces starter dependencies. Each extension comes with dev mode support, hot reload, and native image compatibility.
Learn more: Quarkus Extensions Guide
Managing Dependencies the Quarkus Way
Spring Boot relies on "starters" and can pull in deep transitive dependency trees, sometimes leading to conflicts or hidden costs in your container image.
In Quarkus, dependencies are curated and aligned to a platform BOM, reducing surprises. Here’s how the same functionality looks in pom.xml
:
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.23.0</quarkus.platform.version>
...
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
...
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
The BOM ensures compatibility across extensions. You can rely on Dev Services to spin up databases and services automatically in dev and test mode.
Learn more: Managing Quarkus Dependencies
Migrating JPA Entities and Repositories
In Spring Boot:
@Entity
public class Quote {
@Id @GeneratedValue
private Long id;
private String text;
// Getters/setters omitted for brevity
}
public interface QuoteRepository extends JpaRepository<Quote, Long> {}
In Quarkus, we replace the interface with Panache’s active record pattern:
@Entity
public class Quote extends PanacheEntity {
public String text;
}
This gives us rich, typed access without extra boilerplate:
List<Quote> quotes = Quote.listAll();
Quote q = Quote.find("text like ?1", "%motivation%").firstResult();
Prefer repository-style access? You can also use PanacheRepository<Quote>
if you want more separation of concerns.
Learn more: Quarkus Panache ORM
Rewriting REST Endpoints
Spring Boot controllers use annotations like @RestController
, @GetMapping
, and dependency injection via @Autowired
.
Here’s the Spring version:
@RestController
@RequestMapping("/api/quotes")
public class QuoteController {
@Autowired
private QuoteRepository repository;
@GetMapping
public List<Quote> allQuotes() {
return repository.findAll();
}
}
Here’s how we write it in Quarkus:
@Path("/api/quotes")
@Produces(MediaType.APPLICATION_JSON)
public class QuoteResource {
@GET
public List<Quote> allQuotes() {
return Quote.listAll();
}
}
You can still inject services using @Inject
, but Quarkus prefers constructor or field injection to stay lean.
Learn more: Quarkus REST Guide
Configuration the Quarkus Way
Spring’s application.properties
works similarly to Quarkus, but Quarkus encourages build-time configuration and type-safe config mappings.
From this Spring config:
spring.datasource.url=jdbc:postgresql://localhost:5432/quotes
spring.datasource.username=postgres
spring.datasource.password=secret
To this in Quarkus:
quarkus.datasource.db-kind=postgresql
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/quotes
quarkus.datasource.username=postgres
quarkus.datasource.password=secret
quarkus.hibernate-orm.database.generation=drop-and-create
For more structured config, use @ConfigMapping
:
@ConfigMapping(prefix = "app")
public interface AppConfig {
String mode();
}
Learn more: Quarkus Configuration Reference and ConfigMapping.
Testing in Quarkus
Spring Boot uses @SpringBootTest
for full-context testing, which can be heavy and slow. Quarkus uses @QuarkusTest
, leveraging Dev Services for fast, isolated, container-backed tests. For full blown integration testing you can use @QuarkusIntegrationTest. @QuarkusIntegrationTest should be used to launch and test the artifact produced by the Quarkus build, and supports testing a jar (of whichever type), a native image or container image.
Example test in Quarkus:
@QuarkusTest
public class QuoteResourceTest {
@Test
public void testAllQuotesEndpoint() {
given()
.when().get("/api/quotes")
.then()
.statusCode(200);
}
}
This uses RestAssured and requires no special container config. Quarkus uses Testcontainers under the hood and spins up PostgreSQL automatically in test mode if needed.
Learn more: Quarkus Testing Guide
Spring Compatibility Layer (Optional Migration Aid)
Quarkus includes a Spring Compatibility Layer that can ease the transition by supporting key Spring APIs during migration.
Supported areas include:
@RestController
→ viaquarkus-spring-web
@Service
,@Component
,@Autowired
→ viaquarkus-spring-di
@Repository
,JpaRepository
→ viaquarkus-spring-data-jpa
CrudRepository, JpaRepository
→ viaquarkus-spring-data-rest
This lets you incrementally port your application. You can run Spring-style beans side-by-side with Quarkus beans while gradually refactoring.
Learn more:
Where to Go from Here
Quarkus doesn’t just modernize your stack, it rethinks Java development around speed, containers, and dev happiness. If you’re coming from Spring Boot, you’ll notice:
Cleaner and more intentional dependencies
Better out-of-the-box container behavior
Built-in Dev Services for databases and Kafka
First-class support for native builds via GraalVM or Mandrel
A developer experience that’s honestly fun again
➡️ If you’re a Spring developer…
Start with a real project. Use the Spring Compatibility layer to bring your code over piece by piece. Run in dev mode (./mvnw quarkus:dev
) and experience the live reload magic. Then start replacing your controllers and repositories with Quarkus-native code.
➡️ If you’re an architect or team lead…
Explore Quarkus Platform features like Observability, OpenTelemetry, Kafka, GraphQL, and security. Try building a Quarkus app with native compilation and benchmark the results.
And don’t forget: Quarkus has first-class Kubernetes integration. Deploying cloud-native Java has never been this smooth.
Ready to modernize? Start today at https://quarkus.io
Hi Markus I like your articles, but it is a pity that they are only based in Maven as a build tool, is there no support for Gradle in Quarkus ?