Spring Cloud Config Server provides centralized external configuration management distributed across multiple applications and environments. This comprehensive guide covers setting up, configuring, and using Spring Cloud Config Server in Java applications.
Basic Config Server Setup
1. Config Server Dependencies
Maven Configuration:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
<relativePath/>
</parent>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2022.0.3</spring-cloud.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-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</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>
</project>
2. Basic Config Server Application
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
3. Config Server Configuration
application.yml:
server:
port: 8888
spring:
application:
name: config-server
profiles:
active: native, git
security:
user:
name: config-user
password: config-password
cloud:
config:
server:
# Git backend configuration
git:
uri: https://github.com/my-organization/config-repo
default-label: main
search-paths:
- "applications/{application}"
- "environments/{profile}"
timeout: 5
force-pull: true
# Native file system backend
native:
search-locations:
- "classpath:/config/"
- "file:./config/"
- "file:/etc/config/"
# Composite configuration with multiple backends
composite:
- type: git
uri: https://github.com/my-org/config-repo
- type: vault
host: 127.0.0.1
port: 8200
kv-version: 2
# Encryption configuration
encrypt:
key-store:
location: classpath:keystore.jks
password: storepass
alias: configkey
secret: keypass
# Management endpoints
management:
endpoints:
web:
exposure:
include: health,info,metrics,refresh,bus-refresh
endpoint:
health:
show-details: always
Git Backend Configuration
4. Advanced Git Configuration
spring:
cloud:
config:
server:
git:
uri: https://github.com/my-organization/config-repo
default-label: main
search-paths:
- "applications/{application}"
- "services/{application}"
- "shared/{profile}"
repos:
# Application-specific repositories
my-special-app:
pattern: my-special-app*
uri: https://github.com/my-organization/special-config-repo
search-paths:
- "config"
# Development team repositories
team-a:
pattern: team-a-*
uri: https://github.com/team-a/config-repo
team-b:
pattern: team-b-*
uri: https://github.com/team-b/config-repo
# Git authentication
username: ${GIT_USERNAME}
password: ${GIT_PASSWORD}
passphrase: ${GIT_PASSPHRASE}
private-key: ${GIT_PRIVATE_KEY}
# Git configuration
timeout: 10
basedir: /tmp/config-repo
force-pull: true
delete-untracked-branches: true
clone-on-start: true
5. File System Backend
spring:
profiles:
active: native
cloud:
config:
server:
native:
search-locations:
- "classpath:/config/"
- "file:./config-repo/"
- "file:/etc/application-config/"
- "optional:configserver:${user.home}/config-repo/"
Security Configuration
6. Security Configurations
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/actuator/info").permitAll()
.anyRequest().authenticated()
)
.httpBasic(withDefaults())
.csrf(csrf -> csrf.ignoringRequestMatchers("/actuator/bus-refresh"));
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withUsername("config-user")
.password("{bcrypt}$2a$10$dXJ3SW6G7P.XBLBvanJYc.ZO.8bS5ZL.8L.8.8.8.8.8.8.8.8.8.8.8")
.roles("CONFIG")
.build();
return new InMemoryUserDetailsManager(user);
}
}
7. JWT Security with Config Server
@Configuration
public class JwtSecurityConfig {
@Value("${jwt.secret:defaultSecret}")
private String jwtSecret;
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withSecretKey(
new SecretKeySpec(jwtSecret.getBytes(), "HmacSHA256")
).build();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/actuator/**").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
}
Client Configuration
8. Config Client Setup
Client Dependencies:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
bootstrap.yml (Client):
spring: application: name: my-client-app profiles: active: dev cloud: config: uri: http://localhost:8888 username: config-user password: config-password fail-fast: true retry: initial-interval: 1000 max-interval: 2000 max-attempts: 6 multiplier: 1.1 request-connect-timeout: 5000 request-read-timeout: 5000 # Label for Git branch/tag label: main # Profile-specific configuration profile: dev,eu-west # Discovery first approach discovery: enabled: true service-id: config-server # Enable refresh endpoint management: endpoints: web: exposure: include: health,info,refresh endpoint: refresh: enabled: true
9. Client Application with Refresh Scope
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
@RestController
@RefreshScope
public class ConfigClientController {
@Value("${app.message:Hello default}")
private String message;
@Value("${app.feature.enabled:false}")
private boolean featureEnabled;
@Autowired
private AppConfig appConfig;
@GetMapping("/message")
public String getMessage() {
return message;
}
@GetMapping("/config")
public AppConfig getConfig() {
return appConfig;
}
@PostMapping("/refresh")
public void refresh() {
// Configuration will be refreshed automatically
}
}
@Component
@ConfigurationProperties(prefix = "app")
@RefreshScope
public class AppConfig {
private String name;
private int version;
private DatabaseConfig database;
private List<String> servers = new ArrayList<>();
public static class DatabaseConfig {
private String url;
private String username;
private String password;
private int poolSize;
// Getters and setters
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public int getPoolSize() { return poolSize; }
public void setPoolSize(int poolSize) { this.poolSize = poolSize; }
}
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getVersion() { return version; }
public void setVersion(int version) { this.version = version; }
public DatabaseConfig getDatabase() { return database; }
public void setDatabase(DatabaseConfig database) { this.database = database; }
public List<String> getServers() { return servers; }
public void setServers(List<String> servers) { this.servers = servers; }
}
Configuration Files Structure
10. Git Repository Structure
config-repo/ ├── applications/ │ ├── my-client-app/ │ │ ├── application.yml │ │ ├── application-dev.yml │ │ ├── application-prod.yml │ │ └── application-uat.yml │ └── another-app/ │ ├── application.yml │ └── application-prod.yml ├── shared/ │ ├── application-common.yml │ ├── database-common.yml │ └── security-common.yml └── environments/ ├── dev/ │ └── application.yml ├── prod/ │ └── application.yml └── uat/ └── application.yml
11. Configuration File Examples
application.yml (Shared):
# Common configuration for all applications app: version: 1.0.0 company: "My Company" logging: level: root: INFO com.example: DEBUG org.springframework.cloud: INFO management: endpoints: web: exposure: include: health,info,metrics,refresh endpoint: health: show-details: always
my-client-app.yml:
app:
name: "My Client Application"
message: "Hello from Config Server"
feature:
enabled: true
name: "New Dashboard"
server:
port: 8080
servlet:
context-path: /api
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: ${DB_USERNAME:appuser}
password: ${DB_PASSWORD:apppass}
hikari:
maximum-pool-size: 20
minimum-idle: 5
jpa:
hibernate:
ddl-auto: validate
show-sql: false
custom:
cache:
ttl: 300s
max-size: 1000
retry:
max-attempts: 3
backoff: 1000ms
my-client-app-dev.yml:
app: message: "Hello from Development Environment" feature: enabled: true spring: datasource: url: jdbc:h2:mem:testdb jpa: show-sql: true hibernate: ddl-auto: create-drop logging: level: com.example: DEBUG
my-client-app-prod.yml:
app: message: "Hello from Production Environment" feature: enabled: false spring: datasource: url: jdbc:mysql://prod-db:3306/mydb jpa: show-sql: false hibernate: ddl-auto: validate management: endpoints: web: exposure: include: health,info
Encryption and Decryption
12. Encryption Setup
@Configuration
public class EncryptionConfig {
@Bean
public TextEncryptor textEncryptor(
@Value("${encrypt.key:defaultKey}") String key) {
return Encryptors.text(key, "deadbeef");
}
@Bean
@ConditionalOnProperty(name = "encrypt.key-store.location")
public TextEncryptor keyStoreTextEncryptor(
@Value("${encrypt.key-store.location}") Resource location,
@Value("${encrypt.key-store.password}") String storePass,
@Value("${encrypt.key-store.alias}") String alias,
@Value("${encrypt.key-store.secret}") String secret) {
return Encryptors.text(storePass, secret);
}
}
Using Encryption:
# Encrypted values in configuration
spring:
datasource:
password: '{cipher}FKSAJDFGYOS8F7GHAKJFGHLKERHG0943Y598HKLJDFHGLKJDHFGLKJDHFG'
app:
api-key: '{cipher}AQICAHh...long-encrypted-string...'
13. Encryption Endpoints
@RestController
@RequestMapping("/encrypt")
public class EncryptionController {
@Autowired
private TextEncryptor textEncryptor;
@PostMapping
public String encrypt(@RequestBody String plainText) {
return textEncryptor.encrypt(plainText);
}
@PostMapping("/decrypt")
public String decrypt(@RequestBody String encryptedText) {
return textEncryptor.decrypt(encryptedText);
}
}
Service Discovery Integration
14. Config Server with Eureka
# Config Server with Service Discovery spring: cloud: config: server: git: uri: https://github.com/my-org/config-repo discovery: enabled: true eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ instance: prefer-ip-address: true # Client configuration with discovery spring: cloud: config: discovery: enabled: true service-id: config-server fail-fast: true
Monitoring and Management
15. Health Check and Monitoring
@Component
public class ConfigServerHealthIndicator implements HealthIndicator {
@Autowired
private EnvironmentRepository environmentRepository;
@Override
public Health health() {
try {
// Test configuration retrieval
environmentRepository.findOne("config-server", "default", null);
return Health.up()
.withDetail("repository", "accessible")
.build();
} catch (Exception e) {
return Health.down(e)
.withDetail("repository", "unreachable")
.build();
}
}
}
Custom Actuator Endpoints:
@RestController
@RequestMapping("/actuator/config")
public class ConfigActuatorEndpoint {
@Autowired
private EnvironmentRepository environmentRepository;
@GetMapping("/applications")
public List<String> getApplications() {
// Return list of configured applications
return Arrays.asList("app1", "app2", "app3");
}
@GetMapping("/{application}/{profile}")
public Map<String, Object> getConfiguration(
@PathVariable String application,
@PathVariable String profile) {
Environment env = environmentRepository.findOne(application, profile, null);
Map<String, Object> config = new HashMap<>();
config.put("name", env.getName());
config.put("profiles", env.getProfiles());
config.put("propertySources", env.getPropertySources());
return config;
}
}
High Availability Setup
16. Config Server Cluster
# Multiple Config Server instances with load balancing
spring:
cloud:
config:
server:
git:
uri: https://github.com/my-org/config-repo
# Client configuration for multiple servers
uri:
- http://config-server-1:8888
- http://config-server-2:8888
- http://config-server-3:8888
# With service discovery
eureka:
instance:
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka-server-1:8761/eureka/,http://eureka-server-2:8761/eureka/
Testing Configuration
17. Test Configurations
@SpringBootTest
@ActiveProfiles("test")
public class ConfigServerTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testConfigServerEndpoints() {
ResponseEntity<String> response = restTemplate
.withBasicAuth("config-user", "config-password")
.getForEntity("/my-client-app/default", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Test
public void testEncryption() {
String plainText = "secret-value";
ResponseEntity<String> encrypted = restTemplate
.postForEntity("/encrypt", plainText, String.class);
assertThat(encrypted.getStatusCode()).isEqualTo(HttpStatus.OK);
}
}
@SpringBootTest
public class ConfigClientTest {
@Autowired
private AppConfig appConfig;
@Test
public void testConfigurationProperties() {
assertThat(appConfig.getName()).isEqualTo("My Client Application");
assertThat(appConfig.getVersion()).isEqualTo(1);
}
@TestConfiguration
@EnableConfigurationProperties(AppConfig.class)
static class TestConfig {
}
}
Spring Cloud Bus for Configuration Refresh
18. Config Server with Bus
Dependencies:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
Configuration:
spring: rabbitmq: host: localhost port: 5672 username: guest password: guest management: endpoints: web: exposure: include: bus-refresh
Refresh all clients:
curl -X POST http://config-server:8888/actuator/bus-refresh
Refresh specific client:
curl -X POST http://config-server:8888/actuator/bus-refresh/my-client-app:8080
Best Practices
19. Configuration Management Tips
- Use profiles for environment-specific configurations
- Externalize sensitive data using encryption
- Version control your configuration files
- Use shared configurations for common settings
- Implement proper security with authentication
- Monitor configuration changes and their impact
- Use fail-fast in clients to detect configuration issues early
- Implement retry mechanisms for configuration loading
- Use service discovery for dynamic config server location
- Regularly backup your configuration repository
Spring Cloud Config Server provides a robust solution for centralized configuration management in microservices architectures, enabling dynamic configuration updates, environment-specific settings, and secure credential management across your distributed systems.