Centralized Configuration in Microservices with Spring Cloud Config Server
Learn how to build a Git-backed configuration server using Spring Cloud Config Server and consuming the configuration settings from a Spring Boot application

Unlike a traditional monolithic application in which everything runs within a single instance, a microservice-based application consists of multiple instances of services running across multiple servers. Managing configuration settings for each of these service instances can be challenging as maintaining multiple copies of configuration settings across different servers, locations, and environments is error-prone and difficult to manage. This is especially true for the increasing number of services in microservices architecture and services deployed in the cloud with an auto-scaling feature where servers come and go in the cloud. As a result, need has grown for a better mechanism to manage configuration settings in microservice-based applications. This is where a centralized configuration server steps in to take these configuration settings into a centralized location that is externalized from the distributed services.
In this article, we will learn how to build a centralized configuration server using a Spring Cloud config server that uses Git repository as configuration storage. Will we also look at how to consume the remote configuration settings from a service build using Spring Boot.
Spring Cloud Config Server
A Spring Cloud config server is one of the more popular centralized configuration servers used in a microservice-based application. This is especially true with the increasing trend of Java developers building their application using Spring Boot; providing little to no effort of work to integrate their application to Spring Cloud config server. It uses a typical client and server approach for storing and serving configuration settings across these distributed services.
Prerequisites
Before getting ready to write some code, we need to create two Spring Boot projects: the server and the client project. We will be using Apache Maven in this article. You can use the Spring Initializr website to generate these projects with Spring Boot 2.x dependency. Alternatively you can download the sample server and client project from GitHub.
Building a Configuration Server using Local File System Storage
The server project requires a Spring Cloud config server module as well as Spring Boot Starter Security and a Spring Boot Starter Web module as dependencies.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
If you are starting off with an empty Maven project, remember to add those dependencies into the Maven, POM.xml file for our Config Server. If you are using the Spring Initializr website, remember to add Config Server and Spring Security dependencies to your project.
The Spring Boot Starter Web module is required for any building web application, including RESTful applications using Spring MVC.
The Spring Boot Starter Security module lets us conveniently add and configure a basic authentication realm to our server project without writing any code. This is all thanks to Spring Boot auto-configuration. For the purpose of this demonstration we will configure a username and password in an application.yaml file.
spring:
security:
user:
name: user
password: password
As with any other Spring Boot application, we need a main class to start up the Spring ApplicationContext. Create a ConfigServer class with @SpringBootApplication annotation, with the main entry method calling Spring Boot’s SpringApplication.run() to launch the server. Add the @EnableConfigServer annotation to auto-configure our Config Server.
@EnableConfigServer
@SpringBootApplication
public class Application {
public static final void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
}
Our configuration server runs on port 8080 by default, but we could switch it to any arbitrary port in various ways. Here, we are using the easiest way: configuring the server.port property in our application configuration file, application.yaml to let our server run on port 8888.
server:
port: 8888
Spring Cloud Config Server supports the local file system, Git repository, and HashiCorp Vault as configuration storage. We will first learn how to use a local file system backed configuration store to quickly get our Config Server running and change to use Git repository as configuration storage in the later part. Now, let’s configure our active profile as native with spring.profiles.active property in application.yaml.
spring:
profiles:
active: native
With a native profile, the Spring Cloud Config Server will load the configuration files from the local classpath (or a file system configured with a spring.cloud.config.server.native.searchLocations property). The default search locations are classpath:/, classpath:/config, file:./ and file:./config.
We will use [classpath:./config] as our configuration store location. Create a config directory and a config-client.yaml file in it under our server project’s resources. Put in any arbitrary settings in the config-client.yaml file.
logging:
level:
org.springframework.boot: DEBUG
Hit the run button and see how your Config Server starts up. You will notice from the console that our Config Server is running with a native profile on port 8888.
The following profiles are active: native
...
Tomcat started on port(s): 8888
Started ConfigServer
The only code we need to write in our Config Server is the main entry method to launch our Spring application. The rest of the work is to configure our application profile and some basic security settings in application.yaml; Spring will provision everything else at runtime.
Fetching Configurations using CURL
Now that our Config Server is up and running, we will use CURL (a command-line tool) to fetch the configuration from the server. Open your terminal and run the CURL command to fetch configuration properties for config-client using the default profile.
curl --user user:password http://localhost:8888/config-client/default
We need to provide the configured username and password user:password in our Config Server to request for configuration. As previously configured in our Config Server, the Spring Security module will perform a basic authentication on the username and password before the Spring Cloud Config Server module processes the configuration request.
The request to URL http://localhost:8888/config-client/default will return a JSON response with the requested configuration properties.
{
"name":"config-client",
"profiles":[
"default"
],
"label":null,
"version":null,
"state":null,
"propertySources":[
{
"name":"classpath:/config/config-client.yaml",
"source":{
"logging.level.org.springframework.boot":"DEBUG",
"greeting.message":"hi, this is a message served from local file system config server"
}
}
]
}
The Spring Cloud Config Server module will locate the requested configurations in the following pattern according to the request path parameters:
/{application}/{profile}[/{label}]
/{application}.yaml
/{application}-{profile}.yaml
/{label}/{application}-{profile}.yaml
/{application}.properties
/{application}-{profile}.properties
/{label}/{application}-{profile}.propertiesPlaceholder {application} will map to spring.application.name on the client-side properties.
The placeholder {profile} will map to spring.profiles.active, a comma separated list on the client-side properties. Placeholder {label} will map to the git label at the server-side and is default to master {label} is an optional parameter.
The configuration storage backed with the local classpath or file system that we are currently using is only feasible in a development environment where we have a single instance of Config Server. Additionally, any changes to our configuration settings are not audited with the local file system.
Configuring Git-backed Storage
Spring Cloud Config Server supports the use of the Git repository as its configuration store. It is very convenient to manage upgrades and audit changes to configuration settings with a Git-backed configuration store (in comparison to a local file system). The Spring Cloud Config Server uses a Git-backed configuration store as the default implementation, so to configure our config server to use a Git-backed configuration storage we can either leave native in the active profile or replace it with Git.
spring:
profiles:
active: git
The next thing to do is to configure the location to our Git repository with spring.cloud.config.server.git.URI property in application.yaml.
spring:
cloud:
config:
server:
git:
uri: https://github.com/getstarted-spring/config-store.git
cloneOnStart: true
Here, we will be configuring our config server to use our sample Git repository via the config-store on GitHub. The config-store consists of two YAML configuration files — config-client.yaml and config-client-development.yaml — which will be used in the latter part of our article. So as of now, let’s ignore the content of both files. Notice that in our application.yaml file that we have also instructed our Config Server to clone from the Git repository every time it starts running with the spring.cloud.config.server.cloneOnStart property.
To highlight the differences in our Config Server when running with a native profile (compared to a Git profile) we will configure our logging level for Spring Cloud Config Server to DEBUG level. Please note that this is not related to enabling the Git-backed configuration store.
logging:
level:
org.springframework.cloud.config.server: DEBUG
With everything in place, hit the run button and see how our Config Server runs with a Git-backed configuration storage.
The following profiles are active: git
...
No credentials provider required for URI https://github.com/getstarted-spring/configstore.git
You will notice from the console that our config server is now running a Git profile. There will also be an additional log saying that no credentials provider is required for our configured Git location. In an actual environment, we will always configure a credential for our Git repository for security purposes but to make things simple for this article, we will leave this as it is. Now we have got our Config Server running with a Git-backed configuration store without too much effort.
Consume Configurations from Spring Boot Application
Now that our config server is up and running, we will build a Spring Boot web application to consume configuration from our config server. To make our Config Client web application as simple as possible, we will only use a Spring Boot Starter Web module and a Spring Cloud Starter Config module as its dependencies, ignoring the security mechanism of our web application.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
Again, if you are starting off from an empty Maven project, remember to add those two dependencies into the Maven, POM.xml file. If you are using a Spring Initializr website to create your project, remember to add Config Client and Web dependencies to your project.
Adding the Spring Boot Starter Web module will make our Config Client run as a web application using Spring MVC. The Spring Cloud Starter Config module lets us take advantage of the Spring Cloud Config Server as a Spring Boot application. Without writing any code to fetch configuration from our config server, Spring will provision everything at runtime. When our web application starts, it will fetch its configuration from the configured config server through bootstrap configuration properties and initialize the Spring Environment with remote property sources.
For all this to happen, we need to configure the URI, and configure the username and password to access our config server with spring.cloud.config.uri, spring.cloud.config.username and spring.cloud.config.password properties in our Bootstrap configuration file, bootstrap.yaml.
spring:
cloud:
config:
uri: http://localhost:8888
username: user
password: password
The next important setting in our client application is the application name through spring.application.name property. It is a required parameter to request for configuration properties from Config Server.
spring:
application:
name: config-client
Although it’s not necessary to set an active profile in our config client (as Spring uses a default profile to request for the configuration), we intended for our config client to request for development configuration to show how the Spring Cloud Config Server can locate and serve the configuration properties when an active profile is provided.
spring:
profiles:
active: development
As usual, we need a main class to start up the Spring ApplicationContext. Create a ConfigClient class with @SpringBootApplication annotation, the main entry method calling Spring Boot’s SpringApplication.run() to launch the server.
@SpringBootApplication
public class ConfigClient { public static final void main(final String[] args) {
SpringApplication.run(ConfigClient.class, args);
}
}
Next, we will create a RestController in our Config Client to demonstrate that the remote configuration properties served by our Config Server are property received and initialized into the Spring environment in the application.
Create a ConfigRestController class with @RestController annotation.
@RestController
public class ConfigRestController {
}
Adds a private string variable, greetingMessage with @Value annotation to bind it with a greeting.message property value in Spring environment properties.
@Value("${greeting.message}")
private String greetingMessage;
Finally, add a getGreetingMessage() method with @GetMapping annotation that tells Spring to expose this method as HTTP GET Request with request path “/”. The getGreetingMessage() method will return the bond value from greetingMessage variable.
@GetMapping(path = "/")
public String getGreetingMessage() {
return greetingMessage;
}
That is all for our Config Client. Hit the run button and see how your Config Client application starts up. You will notice from the console that the Config Client’s active profile is developed and running on port 8080.
The following profiles are active: development
...
Tomcat started on port(s): 8080
Started ConfigClient
There will be additional log entries saying that its fetching config from the server and the located environment with config-client name and development profile. These are additional log entries are written by Spring Cloud Config and shows that what we have built so far is working accordingly. Our Config Client is fetching configuration properties from our running Config Server.
Fetching config from server at : http://localhost:8888
Located environment: name=config-client, profiles=[development]
Additionally, you will also notice that the console is filled with DEBUG level log entries from Spring Boot. This is because our config client has been initialized with the fetched logging level configuration property configured in the remote configuration config-client.yaml.
logging:
level:
org.springframework.boot: DEBUG
Now open your favorite terminal and run the CURL command to make an HTTP Get request to our Config Client to get the greeting message.
curl http://localhost:8080
You will get a greeting message, saying it’s a development message and served from Git repository.
hi, this is a development message served from git repository on github
This message is configured at greeting.message property in our remote configuration file, config-client-development.yaml, and is fetched and bonded to the greetingMessage variable. Remember that Spring Cloud Config Server uses the pattern /{application}-{profile}.yaml to locate and served configuration properties according to client provided request parameters.
greeting:
message: hi, this is a development message served from git repository on github
Summary
This article has looked at how to create a centralized configuration server using a Spring Cloud Config Server that uses a local file system or Git repository as configuration storage. We have also learned how to build a simple Spring Boot Web application that consumes the hosted configuration settings using the Spring Cloud Config Starter.
All the source code in this article can be downloaded from GitHub Config-Server, Config-Store and Config-Client.