gRPC moved into Spring Boot 4.1: the new starters, the property renames, and what we measured
DEV Community

gRPC moved into Spring Boot 4.1: the new starters, the property renames, and what we measured

gRPC moved into Spring Boot 4.1: the new starters, the property renames, and what we measured

Spring Boot 4.1 went GA on 10 June 2026, and its biggest change for service-to-service work is that gRPC support moved into Spring Boot itself. The spring-grpc 1.1.0 release notes state it in a single line: "The main change in 1.1.0 is the migration of autoconfiguration to Spring Boot 4.1.0."

Concretely, the starters you add to a build are now Boot's own:

  • org.springframework.boot:spring-boot-starter-grpc-server
  • org.springframework.boot:spring-boot-starter-grpc-client
  • org.springframework.boot:spring-boot-starter-grpc-server-test
  • org.springframework.boot:spring-boot-starter-grpc-client-test

The spring-grpc project lives on as the programming model: spring-grpc-core:1.1.0 supplies @GrpcService, @ImportGrpcClients and GrpcChannelFactory, and it arrives transitively. You never declare spring-grpc in your build.

The old org.springframework.grpc starters are deprecated, with the 1.0.x artifacts still on Maven Central for projects pinned to Boot 4.0. When the merge was announced in November 2025, the plan included explicit migration guidance at release time. What exists as I write is a draft wiki page, and the tutorials that rank for the topic still teach the old coordinates or the abandoned net.devh starter; Baeldung's guide still declares Boot 3.4/3.5 support.

We built a complete server and client on 4.1.0 GA for a full tutorial on our blog; this post is the condensed version of what changed and what we found.

The property renames

The property tree was restructured as well as re-homed. These are the renames most likely to bite when you migrate a spring-grpc 1.0.x project:

spring-grpc 1.0 (Boot 4.0) Spring Boot 4.1
spring.grpc.client.channels.<name>.address spring.grpc.client.channel.<name>.target (singular channel, address becomes target)
spring.grpc.client.channels.<name>.default-deadline spring.grpc.client.channel.<name>.default.deadline
spring.grpc.server.keep-alive.* spring.grpc.server.keepalive.*
spring.grpc.server.max-inbound-message-size spring.grpc.server.inbound.message.max-size
spring.grpc.server.address (combined host:port) spring.grpc.server.address (address only) plus a separate spring.grpc.server.port
spring.grpc.server.health.actuator.* Removed; health is configured under spring.grpc.server.health.*

Each of these was checked against the 4.1.0 GA jars and the migration guide while building the project.

The build file delta is zero lines

This was the finding I expected least. The Spring Initializr skeleton applies com.google.protobuf version 0.9.6 with no protobuf{} block and no codegen configuration at all. Drop a .proto file into src/main/proto/ and the build generates both the message classes and the gRPC service stubs.

It works because Boot 4.1's Gradle plugin ships a ProtobufPluginAction that reacts to the protobuf plugin being applied and configures the protoc artifact (4.34.2, aligned with the managed protobuf-java), the io.grpc:protoc-gen-grpc-java codegen plugin (1.80.0, aligned with the managed gRPC BOM), and a resolution strategy keeping them in step with the runtime classpath.

Every pre-4.1 tutorial showing a 20-line protobuf{} block is describing configuration this setup no longer needs. Across the whole project, server and client, build.gradle never changed from the skeleton.

What runs before you configure anything

With spring-boot-starter-grpc-server on the classpath and one @GrpcService class, startup gives you:

  • A Netty gRPC server on port 9090 alongside Tomcat
  • Reflection registered by default (grpcurl works with just -plaintext)
  • grpc.health.v1.Health answering SERVING out of the box, bridged from Boot's health system when actuator is present

One default deserves attention: spring.grpc.server.observation.enabled is true, so an ObservationGrpcServerInterceptor instruments every call even when nothing consumes the observations. In our A/B it cost roughly 20 to 25% of gRPC throughput with no collector attached (a single clean measurement pair, so treat the exact percentage as indicative). If you export metrics or traces you're paying for something you use; if you don't, that property is the off switch.

The benchmark

We measured unary REST (Spring MVC, JSON) against unary gRPC on the same running process, same H2-backed repository, same row, with k6 and ghz in four order-balanced pairs, both protocols seconds apart within each pair.

Median run:

  • REST: 1,613 req/s with p50/p95/p99 of 7.0 / 27.4 / 38.0 ms
  • gRPC: 1,926 req/s with p50/p95/p99 of 7.9 / 14.0 / 19.7 ms

That is about 19% more throughput and roughly half the tail latency, with medians close to equal.

The environment note belongs next to the numbers, so here it is: this ran on a shared, virtualised 2-vCPU sandbox whose two vCPUs are hyperthread siblings of one physical core, with invisible host throttling we controlled for using a fixed-work CPU probe. The ratios between the two protocols under identical conditions are the result; the absolute figures describe that box and nothing else. The full methodology, fairness notes and raw outputs ship with the code.

The full version

The complete tutorial adds TLS with SSL bundles, error handling with @GrpcAdvice, in-process and real-transport testing, OpenTelemetry tracing with a verified client-to-server trace, and a server-streaming demonstration: gRPC in Spring Boot 4.1: The New Auto-Configuration, End to End. All the code, benchmark scripts and raw results are in the companion repo.

Comments

No comments yet. Start the discussion.