Implementing Centralized Configuration with Spring Cloud Config

Spring Cloud Config provides a centralized mechanism for managing external configurations across distributed systems and microservices. It operates with a server-client architecture. The Config Server, also known as the distributed configuration center, is a standalone microservice responsible for connecting to configuration repositories and offering APIs for clients to retrieve configuration, encrypt, and decrypt information. The Config Clients are the individual microservices or infrastructure components within the microservice architecture that consume these configurations. Upon startup, clients fetch and load their required settings from the designated configuration center.

Spring Cloud Config abstracts the management of environment variables and property configurations for both servers and clients. This makes it adaptable for applications built with technologies other than Spring. By default, the Config Server utilizes Git for storing configuration data, inherently enabling version management for microservice configurations. This allows for convenient management and access via standard Git tooling. Alternative storage options, such as Subversion (SVN) repositories and local file systems, are also supported.

Config Server Reading from Local File System

When using the local repository mode, all configuration files are stored within the Config Server project directory. The Config Server exposes HTTP APIs that clients invoke to retrieve these configuration files.

Project Setup

Begin by creating a main Maven project with Spring Boot version 2.0.3 and Spring Cloud Finchley.RELEASE.


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>com.example</groupId>
   <artifactId>config-parent</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>pom</packaging>

   <name>config-parent</name>
   <description>Parent project for Spring Cloud Config examples</description>

   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.0.3.RELEASE</version>
       <relativePath/> <!-- lookup parent from repository -->
   </parent>

   <properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
       <java.version>1.8</java.version>
       <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
   </properties>

   <dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>
   </dependencies>

   <dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.springframework.cloud</groupId>
               <artifactId>spring-cloud-dependencies</artifactId>
               <version>${spring-cloud.version}</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>

   <repositories>
       <repository>
           <id>aliyunmaven</id>
           <name>Maven Aliyun Mirror</name>
           <url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
           <releases>
               <enabled>true</enabled>
           </releases>
           <snapshots>
               <enabled>false</enabled>
           </snapshots>
       </repository>
   </repositories>

   <build>
       <plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
           </plugin>
       </plugins>
   </build>
</project>
   

Creating the Config Server Module

Within the main Maven project, create a new module named config-server. Ensure its pom.xml inherits from the parent POM and includes the spring-cloud-config-server dependency.


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <artifactId>config-server</artifactId>
   <packaging>jar</packaging>

   <parent>
       <groupId>com.example</groupId>
       <artifactId>config-parent</artifactId>
       <version>0.0.1-SNAPSHOT</version>
       <relativePath/> <!-- lookup parent from repository -->
   </parent>

   <properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
       <java.version>1.8</java.version>
   </properties>

   <dependencies>
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-config-server</artifactId>
       </dependency>

       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>
   </dependencies>

   <build>
       <plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
           </plugin>
       </plugins>
   </build>
</project>
   

Enabling the Config Server

Annotate the main application class with @EnableConfigServer.


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {

   public static void main(String[] args) {
       SpringApplication.run(ConfigServerApplication.class, args);
   }
}
   

Configuring the Config Server

In the application.yml file, configure the server port to 8769, set the application name to config-server, and activate the native profile to indicate that configurations will be read from the local classpath under a shared directory.


server:
 port: 8769
spring:
 application:
   name: config-server
 profiles:
   active: native
 cloud:
   config:
     server:
       native:
         search-locations: classpath:/shared
   

Creating Client Configuration

Create a shared directory within the resources folder of the config server. Inside shared, create a file named config-client-dev.yml. This file will serve as the development environment configuration for a hypothetical config-client application. Set the server port to 8762 and define a property foo with the value foo version 1.


server:
 port: 8762
foo: foo version 1
   

Building the Config Client

Create a new module named config-client. This module will act as a Config Client, fetching its configuration from the Config Server. Its pom.xml should inherit from the parent POM and include the spring-cloud-starter-config dependency. The web starter dependency is already included in the parent POM.


<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
   

Configuring the Config Client

Crucially, Spring Cloud Config related properties must be defined in bootstrap.yml (or bootstrap.properties) because these are loaded before application.yml, ensuring that the configuration server is accessible during the early stages of application startup. Configure the application name as config-client, specify the Config Server's URI as http://localhost:8769, enable fail-fast to halt startup if the config server is unavailable, and set the active profile to dev. The configuration file name is dynamically constructed from the application name and profile, separated by a hyphen (e.g., config-client-dev.yml).


spring:
 application:
   name: config-client
 profiles:
   active: dev
 cloud:
   config:
     uri: http://localhost:8769
     fail-fast: true
   

Creating a Client API Endpoint

Implement a simple REST controller in the config-client project to expose the foo property fetched from the configuration server.


import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

   @Value("${foo}")
   String foo;

   @RequestMapping("/foo")
   public String hi(){
       return foo;
   }
}
   

Start the config-server first, followed by the config-client. The console output for config-client should indicate it's fetching configuration from http://localhost:8769, and the application will start on port 8762. Accessing http://localhost:8762/foo in a browser will display "foo version 1", confirming successful configuration retrieval.

Config Server Reading from Remote Git Repository

To centralize configuration management and enable seamless updates via Spring Cloud Bus, the Config Server can read directly from a remote Git repository. This example uses GitHub.

Modifying Config Server Configuration

Update the application.yml for the Config Server:


server:
 port: 8769
spring:
 application:
   name: config-server
 cloud:
   config:
     server:
       git:
         uri: https://github.com/your-username/your-repo.git
         search-paths: config-repo
       #  username: your_github_username
       #  password: your_github_password
         default-label: main # or master
   

Replace https://github.com/your-username/your-repo.git with your GitHub repository URL. search-paths specifies the subdirectory within the repository containing configurations. If the repository is private, provide username and password. default-label indicates the branch to use (e.g., main or master).

Modify the config-client-dev.yml content (e.g., change the foo value to "foo version 7") and commit it to your GitHub repository. After restarting the config-server and config-client, accessing http://localhost:8762/foo should now reflect the updated value from the Git repository.

Building a Highly Available Config Server

For large-scale deployments, ensuring the availability of the Config Server is crucial. This can be achieved by deploying multiple instances of the Config Server and registering them with Eureka Server.

Setting up Eureka Server

Create an eureka-server module. Its POM should inherit from the parent and include spring-cloud-starter-netflix-eureka-server.


<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
   

Configure application.yml for Eureka Server, setting the port to 8761 and disabling self-registration and registry fetching.


server:
 port: 8761
eureka:
 instance:
   hostname: localhost
 client:
   register-with-eureka: false
   fetch-registry: false
   serviceUrl:
     defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
   

Annotate the main application class with @EnableEurekaServer.


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);
   }
}
   

Configuring Config Server as a Eureka Client

Add the spring-cloud-starter-netflix-eureka-client dependency to the config-server module's POM.


<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
   

In application.yml, specify the Eureka client's service URL:


server:
 port: 8769
spring:
 application:
   name: config-server
 cloud:
   config:
     server:
       git:
         uri: https://github.com/your-username/your-repo.git
         search-paths: config-repo
        # username:
        # password:
         default-label: main
eureka:
 client:
   serviceUrl:
     defaultZone: http://localhost:8761/eureka/
   

Configuring Config Client with Eureka Discovery

Add the Eureka client dependency to the config-client module's POM.

In bootstrap.yml, enable discovery, set the service ID for the config server to config-server, and provide the Eureka client service URL.


spring:
 application:
   name: config-client
 profiles:
   active: dev
 cloud:
   config:
     fail-fast: true
     discovery:
       enabled: true
       service-id: config-server
eureka:
 client:
   serviceUrl:
     defaultZone: http://localhost:8761/eureka/
   

Start the eureka-server. Then, launch two instances of config-server on ports 8768 and 8769. Finally, start the config-client. Observe in the console that the client picks up configurations from one of the available config server instances (e.g., http://localhost:8768). Subsequent restarts of config-client will demonstrate load balancing across the available config-server instances.

Refreshing Configuration with Spring Cloud Bus

Spring Cloud Bus facilitates broadcasting configuration changes and monitoring across distributed nodes using a message broker like RabbitMQ or Kafka. This is particularly useful when dealing with numerous microservice instances, as it eliminates the need for manual restarts after configuration updates.

Why Use Spring Cloud Bus for Configuration Refresh?

Without Spring Cloud Bus, updating configurations for many microservice instances would require restarting each one. Spring Cloud Bus simplifies this by allowing a single trigger (e.g., a POST request to an actuator endpoint) to notify all relevant instances to fetch the latest configuration from the repository.

Prerequisites

Ensure RabbitMQ is installed and running.

Modifying Config Server for Bus Integration

Add the necessary dependencies to the config-server module's POM:


<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
   

Configure application.yml for the Config Server, enabling the bus and exposing the bus-refresh endpoint.


server:
 port: 8769
spring:
 application:
   name: config-server
 cloud:
   config:
     server:
       git:
         uri: https://github.com/your-username/your-repo.git
         search-paths: config-repo
        # username:
        # password:
         default-label: main
   bus:
     trace:
       enabled: true
     enabled: true
eureka:
 client:
   serviceUrl:
     defaultZone: http://localhost:8761/eureka/
management:
 endpoints:
   web:
     exposure:
       include: bus-refresh
   

Modifying Config Client for Bus Integration

Add the following dependencies to the config-client module's POM:


<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
   

Configure bootstrap.yml for the config-client, enabling the bus and exposing the bus-refresh endpoint.


server:
 port: 8763 # Example port for one instance
spring:
 application:
   name: config-client
 profiles:
   active: test
 cloud:
   config:
     fail-fast: true
     discovery:
       enabled: true
       service-id: config-server
   bus:
     trace:
       enabled: true
     enabled: true
eureka:
 client:
   serviceUrl:
     defaultZone: http://localhost:8761/eureka/
management:
 endpoints:
   web:
     exposure:
       include: bus-refresh
   

Applying @RefreshScope

Annotate the beans that need to be refreshed with @RefreshScope. This annotation is essential for the client to receive and apply configuration updates.


import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RefreshScope
@RestController
public class MyController {

   @Value("${foo}")
   String foo;

   @RequestMapping("/foo")
   public String hi(){
       return foo;
   }
}
   

Start eureka-server, then config-server, and finally two instances of config-client on ports 8762 and 8763. Accessing http://localhost:8762/foo and http://localhost:8763/foo should display the current value of the foo property.

Modify the configuration in the remote Git repository (e.g., change foo to "foo version 7"). Send a POST request to http://localhost:8769/actuator/bus-refresh/ (using Postman or a similar tool). After the request is successful, re-accessing the client endpoints (http://localhost:8762/foo and http://localhost:8763/foo) should show the updated value. This demonstrates that a single refresh request to the Config Server, facilitated by Spring Cloud Bus, updates configurations across all connected clients.

The /actuator/bus-refresh endpoint can also be triggered on a Config Client instance. This is useful in scenarios like canary releases where specific clients need configuration updates. By targeting the destination parameter in the refresh request (e.g., /actuator/bus-refresh/{destination}), you can selectively update specific microservice instances.

Tags: spring-boot spring-cloud Spring-Cloud-Config Eureka RabbitMQ

Posted on Fri, 11 Sep 2026 16:03:21 +0000 by bsbotto