
Service discovery with Spring Boot Eureka
In this post, you’ll learn how to set up Spring Boot 2 applications to build a Eureka cluster for Service Registration, and how to make use of Service Discovery via clients that can get each other’s information through the Eureka Registry.
- Introduction
- Setting up a Service Registry with Spring Cloud Eureka
- Eureka Registry Cluster via enabling Peer Awareness
- Registering Eureka Clients
- Eureka Server Cluster (Replica) in Action
Introduction
I wrote this guide because I couldn’t find too much information about how to set this entire configuration up while writing my book. I found some quick guides and valuable information in the official Spring Cloud configuration, but there were still some topics that made me struggle.
So I built an example application, available on GitHub, and accompanied it with this post to explain the code with more details. These are the topics that I’ll cover:
- Build a Eureka Server with Peer Awareness (a Eureka Cluster) using Spring Boot 2 and Spring Cloud Netflix.
- Connect from a Spring Boot 2 application to the Eureka Service Registry, which means how to put Service Discovery into practice.
- Make Spring Cloud Netflix Eureka work with Docker
Quickly jump to the diagram below if you’re curious about how the system looks like.
Setting up a Service Registry with Spring Cloud Eureka
Quick-start
Creating a plain simple Eureka Server is easy with Spring Boot. You can go to start.spring.io and pick Spring Boot 2, the Eureka Server as a dependency and choose Maven if you want to follow my steps. This version uses Spring Boot 2.0.0 and Spring Cloud Finchley.
Open the project in your favorite editor. Then, you just need to add an annotation to your main class, like this:
package com.thepracticaldeveloper.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
If you don’t add any configuration, you’ll get the following by default:
- The default Spring Boot
server.port
property is 8080, so you'll get the Eureka Service Registry available in that port. The dashboard will be accessible athttp://localhost:8080
. - By default, the Eureka Service Registry comes with a client behavior too. That means that it will try to register itself in a registry which, by default, is configured to be at
http://localhost:8761/eureka
. To fix this problem, you can override the property carrying that value (http://localhost:8761/eureka
) or set your server port to 8761. However, it's not useful to have the server registering on its own instance (since we're in standalone mode) so we can just disable it.
Basic configuration
eureka:
client:
registerWithEureka: false
fetchRegistry: false
server:
port: 8761
I recommend you to change the port to 8761, as above. It’s set as default to that value in many configuration classes in Eureka so better use it to avoid extra configuration.
If you run this app with that first version of the configuration, the service registry will start in standalone mode (a single instance). Use your IDE or the command line to execute mvn spring-boot:run
and you’ll get a very sad and lonely service registry. You can navigate with your browser to http://localhost:8761 to check the Eureka Dashboard (we’ll have a look at it later).
This dashboard lists the DS replicas (registry peers) and the registered clients (none, thus far). You can ignore the fact that the own server is listed as an unavailable replica (also part of some default configuration).
Let’s quickly move on, this is just plain simple configuration. Let’s do something more interesting.
Eureka Registry Cluster via enabling Peer Awareness
Resilience with Eureka
What if we want to use the Peer Awareness feature that comes with the Eureka Service Registry? Basically, we’d like to have two registry instances that can share with each other the contents of the registry (the registered clients) so we implement resiliency. To prove that this works, later we’ll register a client in each registry and check if those clients can find each other contacting the instance that they know. See the figure for a better understanding of the scenario.
Eureka’s Peer Awareness: Concepts
For now, we aim to have two Eureka Service Registry instances that can share the registry information: a Eureka Cluster (a.k.a. the Replica mode in Eureka). To achieve this, we need to take into account some very important concepts about how the Eureka Replica mode works:
- The Replica mode will NOT work if you use the same hostname in both instances. That means that you need to give your host two different aliases if you want to test this within the same host.
- The Replica mode will NOT work if you use different application names in both instances. It makes sense since the application itself is the same, it's just that we're replicating it.
- The magic behind the Replica mode is as simple as configuring each instance to register in another one. You can extend this to as many instances as you like, as long as you keep connecting all the edges (A registers in B, B registers in C, C registers in A). In our case, it's just crossing both of them.

Having that in mind, we can use two different Spring Boot profiles for a better-organized configuration.
tpd:
peer1Port: 8761
peer2Port: 8762
management:
endpoint:
health:
enabled: true
show-details: always
shutdown:
enabled: true
endpoints:
web:
base-path: /
# By default, only 'health' and 'info' are accessible via web
exposure:
include: '*'
---
spring:
profiles: peer1
eureka:
instance:
# See blog post for details, modify /etc/hosts
hostname: eureka-peer1
# Either this one or the spring boot name must be the same
# (it works without setting it too, using the alias UNKNOWN)
appname: eureka-cluster
client:
serviceUrl:
defaultZone: http://eureka-peer2:${tpd.peer2Port}/eureka
register-with-eureka: true
fetch-registry: true
server:
port: ${tpd.peer1Port}
---
spring:
profiles: peer2
eureka:
instance:
hostname: eureka-peer2
appname: eureka-cluster
client:
serviceUrl:
defaultZone: http://eureka-peer1:${tpd.peer1Port}/eureka
register-with-eureka: true
fetch-registry: true
server:
port: ${tpd.peer2Port}
Eureka’s Replica Mode configuration explained
Let’s review this configuration:
- There is a common part applicable to both profiles - the part above the first
---
. This is optional, but I prefer to declare two properties to quickly find the exposed ports (tpd.peerNPort
). They are used in the profiles. Also, I've configured Spring Boot Actuator to expose the/health
and/info
endpoints from the root context. This is a good idea in general but, besides that, the Eureka Dashboard creates links to each registered service pointing to these endpoints so you need to configure this if you want them to work (although is a mere aesthetic detail). If you want to use these endpoints, don't forget to add thespring-boot-starter-actuator
dependency to thepom.xml
file. - Note that in this file it's also configured the
/shutdown
endpoint and exposed it via web. This is for educational purposes, don't do this in production :). You can use it later to kill one of the server replicas and see how the peer still has the full registry contents. - The first profile,
peer1
, exposes the server on the port 8761, uses the hosteureka-peer1
and the common application name:eureka-cluster
. You can use the propertyspring.application.name
instead, that will also work. - The second profile,
peer2
, uses the port 8762, a hostnameeureka-peer2
and the commoneureka-cluster
application alias. - Pay attention to the
eureka.client.serviceUrl
property. We create adefaultZone
key in each instance pointing to the other peer (peer2 registers on peer1, and vice-versa). - If you wonder where the
/eureka
context is coming from I tell you that this is hard-coded in a hidden corner of the Eureka implementation. The fact is that you need it since the REST API of the registry is located there and otherwise they won't see each other.
Preparing your system to test it
Before you execute this application you need an extra step though. As pointed out before, the Peer Awareness feature won’t work if you use the same hostname. We set them to eureka-peer1
and eureka-peer2
in our configuration, but we need to redirect those aliases to our real local host for that to work on our machine.
-
In windows, you need to modify your
hosts
file and add a couple of lines like these (note that you usually need administrator privileges and save the file without adding an extension, you can find more info here):127.0.0.1 eureka-peer1 127.0.0.1 eureka-peer2
- In Linux or Mac, you need to add the same lines, but this time in your
/etc/hosts
file (more info on the same link).</li>
Run Eureka Servers in Replica Mode
Now you can run two times this application, again from your preferred IDE or from the command line, using for each execution a different profile:
$ mvn spring-boot:run -Dspring-boot.run.profiles=peer1
$ mvn spring-boot:run -Dspring-boot.run.profiles=peer2
Once they are up and running, navigate with your browser to http://localhost:8761
and http://localhost:8762
respectively. You’ll see the Dashboard showing this time both replicas and where to find them, and also listing them as available.
If you’re getting your Eureka instance as an ‘unavailable replica’, it might be due to one of these reasons:
- You didn't use the same application name in both instances (pay attention to the EUREKA-CLUSTER in the screenshot, what do you see?).
- You missed the context
/eureka
in your defaultZone URLs. - Did you use the same hostname for both instances (e.g. localhost)? Remember that they must be different.
- The hostnames can't be mapped by your computer (did you include them in your hosts file?).
Now that we have our Eureka Cluster, let’s use it to register some dummy clients and see how Service Discovery with Eureka and Peer replication works.
Registering Eureka Clients
Create a Eureka Client
To see Eureka in action and demonstrate that everything is working as expected, we’ll create two Spring Boot applications, each one registering in only one of the instances of the Eureka cluster. We want to prove that the cluster will replicate the registry entries so, in our example, these micro-apps will be able to see each other by using their application alias.
If you want to start from scratch, go to start.spring.io and choose Spring Boot 2, Maven and Eureka Discovery and Web as dependencies. Otherwise, just clone the repository available on GitHub.
Sample service using Eureka Client
We create two different controllers for two different profiles in our client application: spanish
and english
. The idea is that we have two apps communicating with each other:
- When the
HolaController
processes a request tohttp://localhost:8084/hola
, it will try to reachhttp://tpd-en/hello-server
to get an English greeting and concatenate it with its own. - The
HelloController
will do the other way around. It's accessible for us athttp://localhost:8085/hello
, and will build a greeting concatenating the result from the Spanish instance athttp://tpd-es/hola-server
.
We use Service Discovery here for the clients to be able to resolve those aliases: tpd-es
(localhost:8084) and tpd-en
(localhost:8085). This is the implementation of the Spanish controller:
package com.thepracticaldeveloper.eurekaclient.controller;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@Profile("spanish")
public class HolaController {
private final RestTemplate restTemplate;
private final EurekaClient discoveryClient;
private final String englishVirtualAlias;
public HolaController(final RestTemplate restTemplate,
final EurekaClient discoveryClient,
@Value("${tpd.appconfig.english-alias}") final String englishVirtualAlias) {
this.restTemplate = restTemplate;
this.discoveryClient = discoveryClient;
this.englishVirtualAlias = englishVirtualAlias;
}
@GetMapping("/hola-server")
public String holaServer() {
return "Hola desde el cliente en Español!";
}
@GetMapping("/hola")
public String hola() {
final InstanceInfo instance = discoveryClient.getNextServerFromEureka(englishVirtualAlias, false);
final String theOtherResponse = restTemplate.getForObject(instance.getHomePageUrl() + "/hello-server", String.class);
return holaServer() + " My English peer says: " + theOtherResponse;
}
}
As you can imagine, the implementation of the HelloController class (click to open it on GitHub) is similar to this one. We can inject the EurekaClient
bean since it’s loaded in the context just by having the Eureka client dependency in our classpath. Then, we use it to resolve the name of the fixed virtual alias (tpd-en) to the real location of the instance. The logic behind that call will contact the Service Registry (if it hasn’t been previously fetched) and ask for the resolution of the virtual name.
Eureka Client Configuration using Peer Awareness
This client instance is connected to just one of the instances of the Service Registry but, given that we set up the Peer Awareness, that registry instance will contain the mapping of both clients. Let’s have a look at the configuration of the client:
eureka:
instance:
lease-renewal-interval-in-seconds: 10
tpd:
appconfig:
spanish-alias: tpd-es
english-alias: tpd-en
spanish-port: 8084
english-port: 8085
peer1-address: eureka-peer1:8761
peer2-address: eureka-peer2:8762
---
spring:
profiles: spanish
application:
name: ${tpd.appconfig.spanish-alias}
server:
port: ${tpd.appconfig.spanish-port}
# to make it more interesting, let's have each client registering at a different instance
eureka:
client:
service-url:
defaultZone: http://${tpd.appconfig.peer1-address}/eureka
---
spring:
profiles: english
application:
name: ${tpd.appconfig.english-alias}
server:
port: ${tpd.appconfig.english-port}
eureka:
client:
service-url:
defaultZone: http://${tpd.appconfig.peer2-address}/eureka

- The common configuration sets the property
eureka.instance.lease-renewal-interval-in-seconds
to 10 seconds, replacing the default value of 30 seconds. This is completely optional, I normally change it to have clients renewing their lease with the server in a faster way, which is more useful when you're testing the configuration values. - Hanging from
tpd.config
I've extracted some property values like each instance's port, the server URLs and the virtual aliases to use. These are used in the profiles below. - As detailed above, the
spanish
profile sets the application name to the virtual alias (so it will be registered like that in the Eureka Registry). Note that theservice-url
property is set to the server's peer1 instance, whereas theenglish
client instance (identified by the second profile) connects to the server's peer2 instance.
Eureka Server Cluster (Replica) in Action
Run the Eureka Servers and Clients
Time to play with the applications! You can use your IDE to run the four applications but I’ll give you also the instructions to do that from the command line.
Within the eureka-server-example folder, run these commands in two different terminals:
eureka-server-example$ mvn spring-boot:run -Dspring-boot.run.profiles=peer1
eureka-server-example$ mvn spring-boot:run -Dspring-boot.run.profiles=peer2
And now, the client instances, which are also two different commands to run the same application:
eureka-client$ mvn spring-boot:run -Dspring-boot.run.profiles=spanish
eureka-client$ mvn spring-boot:run -Dspring-boot.run.profiles=english
In the background, these four Spring Boot application instances will start communicating with each other thanks to Eureka and our fine-tuned configuration:
- The server instances will register on each other and then synchronize their registries.
- The client instances will register each one on one registry instance, causing them to synchronize their registries again.
Eureka Dashboard showing Server replica and Clients
Navigate with your browser to one of the Registry instances to very that everything went as expected. You should see something like this:
Testing the basics
Finally, if you do a GET request to our exposed endpoints for each client instance (also with your browser, or you can go for curl
or http
if you know how), you’ll see how our Eureka plumbing works perfectly. For example, if you request http://localhost:8084/hola
:
Hola desde el cliente en Español! My English peer says: Hello from the English client!
Getting a 500 error instead? You impatient! Give Eureka some extra time to register the clients and propagate the registry contents.
Peer awareness in Eureka Clients
Bear in mind that, in order to demonstrate that Peer Awareness works, we didn’t use the optimal configuration here. To really achieve resilience, each client should know how to fetch both registries, so our final setup for a production-ready system should specify both instances in the clients (like shown below). However, if we would have done from the beginning, the registry replication wouldn’t have been so obvious to us.
eureka:
client:
service-url:
defaultZone: http://${tpd.appconfig.peer1-address}/eureka,http://${tpd.appconfig.peer2-address}/eureka
Great! You just learned how to set up Service Discovery with Resiliency using Eureka and Spring Boot. Of course, there is much more to learn in terms of Microservices Architecture with Spring Boot and Spring Cloud, like how to use Load Balancing or a Circuit Breaker in this scenario. Also, an API Gateway will be useful if we want to have the same entry point from our browser to access /hello and /hola. If you want to learn all these topics from a practical perspective, have a look at my book Learn Microservices with Spring Boot, I’m sure you’ll enjoy the step-by-step approach used there.
Liked this guide? Then you can star the code on GitHub or leave a comment here, thanks!
Bonus: Docker configuration
If you want to run this example architecture, you can also do it using Docker. First, you need to generate the jar files by executing mvn clean package
in both client and server folders. After that, go to the docker folder in the repository and execute:
docker-compose up
The images will be built and then executed as containers. Note that you don’t need to assign host aliases in this case since that’s now managed by Docker. Enjoy it!
Comments