Eureka is the survivor of the Spring Cloud Netflix stack. While Hystrix, Zuul, and Ribbon were removed from Spring Cloud years ago, Eureka server and client are still actively maintained and ship in every current release train. This post — a complete rewrite of my 2020 Eureka series — builds a working setup on Spring Boot 3.x: a Eureka server, a registered student service, and a second service that discovers and calls it through Spring Cloud LoadBalancer, with none of the deprecated pieces.
Tested with: Spring Boot 3.4, Spring Cloud 2024.0, Java 21. For migrating off the dead Netflix components, see the Spring Cloud Netflix migration guide.
What Changed Since the Spring Boot 2.x Era
| 2020-era setup | Spring Boot 3.x setup |
|---|---|
| Ribbon load-balances client calls | Spring Cloud LoadBalancer (transparent via @LoadBalanced) |
| @LoadBalanced RestTemplate | @LoadBalanced RestClient.Builder (or WebClient) |
| @EnableEurekaClient required | Not needed — client auto-registers when on the classpath |
| spring-cloud-starter-netflix-* everywhere | Only eureka-server / eureka-client starters remain valid |
| javax.* namespace | jakarta.* namespace |
Step 1: The Eureka Server
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2024.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
@SpringBootApplication
@EnableEurekaServer
public class DiscoveryServerApplication {
public static void main(String[] args) {
SpringApplication.run(DiscoveryServerApplication.class, args);
}
}
server:
port: 8761
eureka:
client:
register-with-eureka: false # the server is not its own client
fetch-registry: false
server:
enable-self-preservation: true # keep ON in production (see pitfalls)
Start it and open http://localhost:8761 — the familiar dashboard, now on Boot 3.
Step 2: The Student Service (Eureka Client)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
No @EnableEurekaClient annotation needed any more — presence on the classpath plus configuration is enough:
spring:
application:
name: student-service # this becomes the service ID in Eureka
server:
port: 0 # random port — instances scale without port conflicts
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
prefer-ip-address: true
instance-id: ${spring.application.name}:${random.uuid}
@RestController
@RequestMapping("/students")
public class StudentController {
private static final List<Student> STUDENTS = List.of(
new Student(1, "Asha", "Computer Engineering"),
new Student(2, "Rahul", "Information Technology"));
@GetMapping
public List<Student> all() {
return STUDENTS;
}
}
// Java 21 record — no Lombok needed
record Student(int id, String name, String branch) {}
Step 3: Calling the Service by Name
This is where Ribbon used to live. Today, spring-cloud-starter-loadbalancer (pulled in transitively by the Eureka client starter) resolves service IDs. The consumer side:
@Configuration
public class ClientConfig {
@Bean
@LoadBalanced // service IDs like http://student-service now resolve via Eureka
RestClient.Builder loadBalancedRestClientBuilder() {
return RestClient.builder();
}
}
@Service
public class StudentClient {
private final RestClient restClient;
public StudentClient(@LoadBalanced RestClient.Builder builder) {
this.restClient = builder.baseUrl("http://student-service").build();
}
public List<Student> fetchStudents() {
return restClient.get().uri("/students")
.retrieve()
.body(new ParameterizedTypeReference<List<Student>>() {});
}
}
Run two instances of student-service and watch successive calls alternate between ports in the logs — round-robin balancing with zero Ribbon code. If you prefer declarative clients, the same service ID works with HTTP interface clients or OpenFeign.
Sample Output
GET http://localhost:8080/aggregate/students
[{"id":1,"name":"Asha","branch":"Computer Engineering"},
{"id":2,"name":"Rahul","branch":"Information Technology"}]
# consumer log, alternating instances:
Resolved student-service -> 192.168.1.7:53412
Resolved student-service -> 192.168.1.7:53488
Pitfalls
Self-preservation panic. When Eureka stops receiving heartbeats from many instances at once (a network blip), it stops evicting anyone and shows a red warning. Teams routinely disable self-preservation because the warning looks scary — then a real network partition evicts the whole registry. Keep it on in production.
Stale instances after crash. Eureka clients deregister gracefully on shutdown, but a killed pod stays registered until heartbeat eviction (~90 s by default). Your client-side retry/circuit breaker must tolerate hitting a dead instance during that window — discovery is not health checking.
UnknownHostException on service names. If http://student-service fails to resolve, your RestClient bean was built without the @LoadBalanced builder. The annotation must be on the builder injection point.
Do you need Eureka on Kubernetes? Usually not — a Kubernetes Service already gives you discovery and balancing. Eureka still earns its place on VMs, bare metal, or mixed environments.
AI Prompts for Eureka Setups
Modernize Old Config
Here is my Spring Boot 2.x Eureka + Ribbon configuration: [paste here]. Convert it to Spring Boot 3.x with Spring Cloud LoadBalancer: remove dead Netflix properties, map ribbon.* settings to their LoadBalancer equivalents, and flag anything with no equivalent.
What it does: Cleans a legacy configuration in one pass and surfaces the Ribbon features that genuinely disappeared.
When to use it: During a Boot 2 → 3 upgrade of any Eureka-registered service.
Tune Heartbeats and Eviction
My services take [X] seconds to deploy and I run [N] instances per service. Recommend Eureka lease-renewal, lease-expiration, and registry-fetch intervals that balance failover speed against registry churn, and explain the trade-off of each value.
What it does: Replaces the default 30/90-second timings with values reasoned from your actual deployment cadence.
When to use it: When stale-instance errors or slow failover show up in production.
Decide: Eureka vs Platform Discovery
My deployment targets are: [describe VMs/Kubernetes/hybrid here]. Compare Eureka against platform-native discovery for this setup — operational cost, failover behaviour, multi-cluster reach — and recommend one with justification.
What it does: Forces an explicit architecture decision instead of cargo-culting Eureka into Kubernetes.
When to use it: At architecture-review time, before adding a discovery server to a new system.
Add Resilience to Service Calls
This service calls others via @LoadBalanced RestClient: [paste client code here]. Add Resilience4j circuit breakers and retries appropriate for discovery-based calls, accounting for Eureka’s eviction delay window.
What it does: Wires the resilience layer that discovery alone does not provide.
When to use it: Right after wiring any inter-service call.
Diagnose Registration Issues
My service does not appear in the Eureka dashboard. Here are its application.yml and startup logs: [paste here]. Diagnose the registration failure and list the configuration fixes in order of likelihood.
What it does: Pattern-matches the usual suspects — wrong defaultZone, missing application name, network/port issues.
When to use it: Whenever the dashboard is empty and the logs are unhelpful.
Conclusion
Eureka on Spring Boot 3.x is leaner than the setup you remember: no Ribbon, no @EnableEurekaClient, no RestTemplate — just the server starter, the client starter, and a @LoadBalanced RestClient.Builder. Keep self-preservation on, pair discovery with client-side resilience, and reach for platform-native discovery only when your platform actually provides one.
See Also
- Spring Cloud Netflix to Modern Alternatives: Complete Migration Guide
- Resilience4j Circuit Breaker in Spring Boot
- Spring Boot 4 HTTP Service Clients
- RestTemplate to RestClient Migration Guide
- Zuul to Spring Cloud Gateway Migration Guide