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.