Quarkus · Migration
Migrating from Spring Boot to Quarkus: a practical guide
Migrate a Spring Boot service to Quarkus step by step: annotation mapping, REST, config profiles, Panache, Spring compatibility extensions and common pitfalls.
Moving a service from Spring Boot to Quarkus is less dramatic than it sounds: the concepts — beans, configuration, REST endpoints, repositories, transactions — map almost one to one. This guide shows the mapping, side-by-side code, the Quarkus rules that trip Spring developers up, and a migration plan that keeps risk low.
Plan the migration first
- Start with one service, ideally a small, well-tested one — not the monolith.
- Inventory your Spring starters and find the matching Quarkus extensions (for example with
quarkus extension list). Anything without an equivalent needs a decision early. - Keep the contract identical: same REST paths, same Kafka topics and payloads, same database schema. Your existing integration tests become the safety net.
- Run in JVM mode first. Only consider a native build once everything works on the JVM.
The annotation mapping
| Spring Boot | Quarkus |
|---|---|
@Component, @Service | @ApplicationScoped |
@Autowired / constructor injection | @Inject / constructor injection |
@Value | @ConfigProperty |
@RestController, @RequestMapping | @Path |
@GetMapping, @PostMapping | @GET, @POST |
@PathVariable, @RequestParam | @PathParam, @QueryParam |
| Spring Data JPA repositories | Hibernate ORM with Panache |
@Scheduled | @Scheduled (scheduler extension) |
@SpringBootTest | @QuarkusTest |
application.yml with profiles | application.properties with %dev. / %test. / %prod. prefixes |
REST endpoints
@RestController
@RequestMapping("/orders")
public class OrderController {
private final OrderService service;
public OrderController(OrderService service) {
this.service = service;
}
@GetMapping("/{id}")
public Order get(@PathVariable String id) {
return service.find(id);
}
}@Path("/orders")
@Produces(MediaType.APPLICATION_JSON)
public class OrderResource {
@Inject
OrderService service;
@GET
@Path("/{id}")
public Order get(@PathParam("id") String id) {
return service.find(id);
}
}Configuration and profiles
@ConfigProperty(name = "orders.page-size", defaultValue = "20")
int pageSize;quarkus.datasource.db-kind=postgresql
%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://db:5432/orders
orders.page-size=50In dev and test, Dev Services starts a PostgreSQL container for you automatically when no URL is configured — which is why the URL above only applies to the prod profile. If you prefer YAML, add the quarkus-config-yaml extension.
Data access with Panache
Panache keeps Hibernate ORM but removes boilerplate. In the active-record style, queries live on the entity:
@Entity
public class Order extends PanacheEntity {
public String customer;
public String status;
public static List<Order> findOpen() {
return list("status", "OPEN");
}
}If you prefer repositories, implement PanacheRepository<Order> instead. Transactions use @Transactional from jakarta.transaction.
The Spring compatibility shortcut
Quarkus provides Spring compatibility extensions — for dependency injection, web, Data JPA and Boot-style properties — so many Spring annotations work unchanged. They are a useful bridge for migrating in steps, but they cover the common subset of Spring, not all of it. Plan to move to native Quarkus APIs over time.
Pitfalls Spring developers hit
- Build-time configuration. Some Quarkus properties are fixed when the application is built and can’t be changed with an environment variable at runtime. The configuration reference marks them.
- Runtime classpath scanning. Libraries that discover classes or use heavy reflection at runtime may need an extension or extra configuration — especially for native builds.
- Security is different. Spring Security configuration doesn’t carry over; Quarkus uses its own security model and extensions such as OIDC. Re-test every protected endpoint.
- Dev Services needs Docker (or another container runtime) on developer machines and CI.
- Bean scopes:
@ApplicationScopedbeans are lazily created proxies, while@Singletonbeans are not proxied — choose deliberately.
Migration checklist
- Pick one small, well-tested service.
- Map every Spring starter to a Quarkus extension.
- Port endpoints, config, persistence and scheduling using the mapping above.
- Re-implement security and re-test every protected endpoint.
- Run the existing integration and contract tests unchanged.
- Deploy in JVM mode; consider native once it’s stable.