WL
Java Full Stack Developer
Wassim Lagnaoui

Configuration Management

Master centralized configuration management with Spring Cloud Config Server for consistent, secure, and scalable microservices.

Introduction

Problems with local configuration files in microservices

Local configuration files create significant management challenges in microservices architectures where you might have dozens or hundreds of services, each with their own application.properties or application.yml files. When configuration is scattered across individual services, making environment-specific changes requires updating and redeploying multiple services simultaneously, creating coordination nightmares and increasing deployment risk. Configuration drift becomes inevitable as different services end up with slightly different versions of what should be shared settings like database URLs, API keys, or feature flags. Additionally, managing sensitive information like passwords and API keys becomes a security liability when they're embedded in local files that must be distributed to every service instance.

Why centralized configuration is needed

Centralized configuration management addresses these challenges by providing a single source of truth for all application settings across your entire microservices ecosystem. Instead of hunting through dozens of repositories to change a database connection string, you update it once in the central configuration store and all affected services pick up the change automatically. This approach enables better security practices by keeping sensitive configurations in a secure, centralized location with proper access controls rather than scattered across multiple codebases. Centralized configuration also supports advanced deployment patterns like blue-green deployments and canary releases by allowing you to update configurations without rebuilding or redeploying services.

Benefits: consistency, dynamic refresh, environment management

Consistency is achieved because all services pull their configuration from the same centralized source, eliminating configuration drift and ensuring that shared settings remain synchronized across your entire system. Dynamic refresh capabilities allow you to update application behavior in real-time without service restarts, enabling features like emergency feature toggles, performance tuning, and quick fixes to production issues. Environment management becomes much simpler because you can maintain separate configuration sets for development, testing, staging, and production environments in a single repository, with automatic promotion pipelines that ensure configurations are tested and validated before reaching production. This centralized approach also provides better observability into configuration changes through audit trails and version control integration.


Spring Cloud Config Server Overview

What it is and why it is used

Spring Cloud Config Server is a centralized configuration management solution that provides HTTP-based access to configuration properties for distributed Spring Boot applications. It acts as a configuration repository that serves application properties to multiple microservices from a single, manageable location, typically backed by Git for version control and audit trails. The Config Server integrates seamlessly with Spring Boot applications through simple annotations and provides features like environment-specific configurations, property encryption, and dynamic refresh capabilities. This approach eliminates the need to rebuild and redeploy services when configuration changes are needed, significantly improving operational efficiency and reducing deployment risks in production environments.

Git-backed configuration repository

The Config Server uses Git repositories as its backend storage, leveraging Git's powerful version control features to track configuration changes, enable rollbacks, and provide audit trails for compliance requirements. Configuration files are stored as standard Spring Boot property files (application.yml or application.properties) in Git repositories, making them easily manageable by development teams using familiar tools. Git's branching model allows you to maintain different configuration versions for different environments or feature branches, and changes can be reviewed through standard pull request workflows before being applied to production systems. The Git integration also enables automatic synchronization and backup of configuration data, ensuring high availability and disaster recovery capabilities for your configuration management system.

Profiles support: dev, test, prod

Spring Cloud Config Server provides comprehensive support for Spring profiles, allowing you to maintain environment-specific configurations in a structured, organized manner within the same Git repository. Configuration files follow Spring Boot naming conventions like application-dev.yml, application-test.yml, and application-prod.yml, where each file contains settings specific to that environment. The Config Server automatically serves the appropriate configuration based on the active profile specified by client applications, ensuring that services always receive the correct settings for their current environment. This profile-based approach eliminates the complexity of managing separate configuration repositories for different environments while maintaining clear separation of concerns and reducing the risk of accidentally applying development settings to production systems.

Config Server Setup Example

@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication.class, args);
    }
}
# Config Server application.yml
server:
  port: 8888

spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://github.com/your-org/config-repo.git
          clone-on-start: true
          default-label: main
        health:
          repositories:
            microservices-config:
              label: main
              name: user-service,order-service
              profiles: dev,test,prod

management:
  endpoints:
    web:
      exposure:
        include: health,info,env

Git Repository Structure

# Config repository structure
config-repo/
├── application.yml              # Global defaults
├── application-dev.yml          # Development environment
├── application-test.yml         # Testing environment
├── application-prod.yml         # Production environment
├── user-service.yml            # Service-specific defaults
├── user-service-dev.yml        # User service dev config
├── user-service-prod.yml       # User service prod config
├── order-service.yml           # Order service defaults
└── order-service-prod.yml      # Order service prod config

Config Clients

Connecting microservices as Config Clients

Microservices connect to Spring Cloud Config Server as clients by adding the spring-cloud-starter-config dependency and configuring the Config Server location in their bootstrap.yml or bootstrap.properties files. The client configuration includes the Config Server URL, application name, and active profiles, which the Config Server uses to determine which configuration files to serve. During application startup, Config Clients automatically fetch their configuration from the Config Server before initializing the Spring context, ensuring that all configuration properties are available when the application starts. This automatic integration means that existing Spring Boot applications can be converted to use centralized configuration with minimal code changes, requiring only dependency additions and bootstrap configuration.

Service-specific configuration

Each microservice can have its own dedicated configuration files in the Git repository, following naming conventions that combine the service name with environment profiles for maximum flexibility. Service-specific configurations override global defaults, allowing you to define common settings in application.yml while customizing specific services through files like user-service.yml or order-service-prod.yml. The Config Server applies a property resolution hierarchy where service-specific properties take precedence over global ones, and environment-specific properties override defaults, providing a flexible yet predictable configuration model. This approach enables you to maintain both shared configurations for cross-cutting concerns and service-specific settings for unique requirements without duplication or conflicts.

Refreshing configuration at runtime (@RefreshScope and /actuator/refresh)

The @RefreshScope annotation allows Spring beans to be reinitialized with new configuration values when a refresh event occurs, enabling dynamic configuration updates without application restarts. Beans annotated with @RefreshScope are recreated when the /actuator/refresh endpoint is called, picking up any configuration changes that have been made in the Config Server since the last refresh. This mechanism is particularly valuable for feature flags, operational parameters, and non-structural configuration changes that need to take effect immediately in production environments. However, not all configuration changes can be applied dynamically—structural changes like server ports or database connection pools typically still require application restarts, so the refresh mechanism works best for business logic parameters and feature toggles.

Config Client Setup Example

<!-- pom.xml 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 (loads before application.yml)
spring:
  application:
    name: user-service
  profiles:
    active: dev
  cloud:
    config:
      uri: http://localhost:8888
      fail-fast: true
      retry:
        initial-interval: 1000
        multiplier: 1.1
        max-attempts: 6

management:
  endpoints:
    web:
      exposure:
        include: refresh,health,info

Using Configuration in Application

@RestController
@RefreshScope // Enables dynamic refresh of this bean
public class UserController {
    
    @Value("${app.feature.user-registration:true}")
    private boolean userRegistrationEnabled;
    
    @Value("${app.limits.max-users:1000}")
    private int maxUsers;
    
    @Autowired
    private UserServiceConfig config; // Configuration properties class
    
    @GetMapping("/users")
    public ResponseEntity<String> getUsers() {
        if (!userRegistrationEnabled) {
            return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                .body("User registration is currently disabled");
        }
        
        // Use configuration in business logic
        if (userRepository.count() >= maxUsers) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN)
                .body("Maximum user limit reached");
        }
        
        return ResponseEntity.ok("Users retrieved successfully");
    }
}

@ConfigurationProperties(prefix = "app.user-service")
@RefreshScope
@Component
public class UserServiceConfig {
    
    private boolean registrationEnabled = true;
    private int maxUsers = 1000;
    private String welcomeMessage = "Welcome to our platform!";
    
    // Getters and setters
    public boolean isRegistrationEnabled() { return registrationEnabled; }
    public void setRegistrationEnabled(boolean registrationEnabled) { 
        this.registrationEnabled = registrationEnabled; 
    }
    
    public int getMaxUsers() { return maxUsers; }
    public void setMaxUsers(int maxUsers) { this.maxUsers = maxUsers; }
    
    public String getWelcomeMessage() { return welcomeMessage; }
    public void setWelcomeMessage(String welcomeMessage) { 
        this.welcomeMessage = welcomeMessage; 
    }
}

Configuration Files in Git Repository

# user-service.yml (global defaults for user service)
app:
  user-service:
    registration-enabled: true
    max-users: 1000
    welcome-message: "Welcome to our platform!"
  
logging:
  level:
    com.example.userservice: INFO

# user-service-dev.yml (development overrides)
app:
  user-service:
    max-users: 100
    welcome-message: "Welcome to DEV environment!"

logging:
  level:
    com.example.userservice: DEBUG
    
# user-service-prod.yml (production overrides)
app:
  user-service:
    max-users: 10000
    welcome-message: "Welcome to our production platform!"

logging:
  level:
    com.example.userservice: WARN

Security

Securing Config Server with Basic Auth

Basic Authentication provides a simple but effective way to secure your Config Server by requiring clients to present valid username and password credentials before accessing configuration data. This approach is suitable for internal networks or development environments where you need basic access control without complex authentication infrastructure. The Config Server can be secured using Spring Security's HTTP Basic authentication, and clients authenticate by including credentials in their bootstrap configuration or through environment variables. While Basic Auth is easy to implement and understand, it should be combined with HTTPS/TLS encryption to protect credentials in transit, and credentials should be rotated regularly to maintain security.

Securing Config Server with OAuth2

OAuth2 integration provides enterprise-grade security for Config Servers by leveraging token-based authentication that integrates with existing identity providers and authorization servers. This approach is ideal for organizations that already have OAuth2 infrastructure in place, as it provides centralized authentication management, token expiration, and scope-based access control. Config Clients can authenticate using client credentials flow or other OAuth2 flows depending on your security requirements, and tokens can be automatically refreshed to maintain long-running connections. OAuth2 also enables fine-grained access control where different services can have different scopes or permissions for accessing specific configuration namespaces or profiles.

Securing Config Server with JWT

JWT (JSON Web Token) authentication provides a stateless, scalable security mechanism where Config Clients present signed tokens that contain identity and authorization information without requiring server-side session storage. This approach is particularly valuable in microservices environments because JWT tokens can carry service identity, environment information, and access permissions in a standardized format that can be validated without external dependencies. JWT tokens can be issued by your identity provider or authorization server and include expiration times, issuer information, and custom claims that determine which configurations a client can access. The stateless nature of JWT makes it ideal for horizontally scaled Config Server deployments where token validation doesn't require shared state between server instances.

Encrypting sensitive properties and decrypting them in clients

Property encryption protects sensitive information like database passwords, API keys, and certificates by storing them in encrypted form in the Git repository and automatically decrypting them when served to authorized clients. Spring Cloud Config supports both symmetric and asymmetric encryption, allowing you to choose the appropriate security level for your environment and compliance requirements. Encrypted properties are prefixed with {cipher} in configuration files and are transparently decrypted by the Config Server or client applications depending on your configuration. This approach ensures that sensitive data is never stored in plain text in version control systems while maintaining the convenience of centralized configuration management for security-critical applications.

Basic Auth Security Example

# Config Server with Basic Auth
spring:
  security:
    user:
      name: configadmin
      password: ${CONFIG_SERVER_PASSWORD:secret123}
      roles: ADMIN

# Client configuration for Basic Auth
spring:
  cloud:
    config:
      uri: http://localhost:8888
      username: configadmin
      password: ${CONFIG_SERVER_PASSWORD:secret123}

OAuth2 Security Example

# Config Server OAuth2 Resource Server
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth-server.com/auth/realms/microservices

# Client OAuth2 configuration
spring:
  cloud:
    config:
      uri: http://localhost:8888
  security:
    oauth2:
      client:
        registration:
          config-client:
            client-id: config-client
            client-secret: ${CLIENT_SECRET}
            authorization-grant-type: client_credentials
            scope: config.read
        provider:
          auth-server:
            token-uri: https://auth-server.com/auth/realms/microservices/protocol/openid-connect/token

Property Encryption Example

# Encrypting a property value
curl http://localhost:8888/encrypt -H "Content-Type: text/plain" -d "my-secret-password"
# Returns: 682bc583fbc02f8c9f9ebcfcb6bb8a6c4b5a4f2d8c9f0e5b3d7a1e6c8f2b9d4a
# Configuration file with encrypted properties
database:
  username: dbuser
  password: '{cipher}682bc583fbc02f8c9f9ebcfcb6bb8a6c4b5a4f2d8c9f0e5b3d7a1e6c8f2b9d4a'

api:
  key: '{cipher}a7b3c9d2e5f8g1h4i7j0k3l6m9n2o5p8q1r4s7t0u3v6w9x2y5z8a1b4c7d0e3f6'
# Config Server encryption configuration
encrypt:
  key: mySecretKey # Symmetric key (for development)
  # For production, use asymmetric encryption:
  # key-store:
  #   location: classpath:config-server.jks
  #   password: keystorePassword
  #   alias: configServerKey
  #   secret: keyPassword

High Availability

Setting up multiple Config Server instances

High availability for Config Servers requires running multiple instances across different availability zones or data centers to ensure configuration services remain available even during infrastructure failures or maintenance windows. Each Config Server instance should be configured identically with the same Git repository backend and security settings, allowing them to serve as perfect replicas that can handle client requests interchangeably. Container orchestration platforms like Kubernetes or Docker Swarm can automatically manage Config Server instances, including health checks, automatic restarts, and rolling deployments. Multiple instances also improve performance by distributing the load of configuration requests across several servers, reducing response times and improving overall system resilience during high-traffic periods.

Load balancing between Config Servers

Load balancing distributes configuration requests across multiple Config Server instances using strategies like round-robin, least connections, or health-based routing to ensure optimal resource utilization and response times. Client-side load balancing can be implemented using Spring Cloud LoadBalancer or Ribbon, allowing Config Clients to automatically discover and distribute requests among available Config Server instances. Server-side load balancing using tools like NGINX, HAProxy, or cloud load balancers provides additional benefits like SSL termination, request routing based on client characteristics, and centralized monitoring of Config Server health. Proper load balancing configuration should include health checks that remove unhealthy Config Server instances from the rotation and failover mechanisms that ensure clients can always reach at least one healthy server instance.

Monitoring configuration refresh and errors

Comprehensive monitoring of Config Server operations includes tracking configuration retrieval requests, refresh operations, Git repository synchronization status, and client connection patterns to ensure the system operates reliably. Metrics should cover response times, error rates, Git fetch operations, and the success/failure rates of configuration refresh attempts across all client services. Alerting should be configured for critical events like Config Server unavailability, Git repository access failures, configuration refresh errors, and unusual patterns in configuration requests that might indicate security issues. Log aggregation and analysis help identify trends in configuration usage, detect potential performance issues, and provide audit trails for compliance requirements, while application performance monitoring tools can track the end-to-end impact of configuration changes on service behavior.

High Availability Setup Example

# Config Server HA configuration
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://github.com/your-org/config-repo.git
          clone-on-start: true
          timeout: 10
          refresh-rate: 30
        health:
          enabled: true
          repositories:
            config:
              label: main
              name: application

eureka:
  client:
    service-url:
      defaultZone: http://eureka1:8761/eureka/,http://eureka2:8761/eureka/
  instance:
    prefer-ip-address: true
    health-check-url-path: /actuator/health

management:
  endpoints:
    web:
      exposure:
        include: health,info,env,refresh,metrics
  metrics:
    export:
      prometheus:
        enabled: true

Client-Side Load Balancing

# Client configuration for HA Config Servers
spring:
  application:
    name: user-service
  cloud:
    config:
      discovery:
        enabled: true
        service-id: config-server
      fail-fast: true
      retry:
        initial-interval: 1000
        multiplier: 1.1
        max-attempts: 6
        max-interval: 2000

eureka:
  client:
    service-url:
      defaultZone: http://eureka1:8761/eureka/,http://eureka2:8761/eureka/

Monitoring and Alerting Configuration

# Prometheus monitoring for Config Server
management:
  metrics:
    tags:
      application: ${spring.application.name}
      environment: ${spring.profiles.active}
    export:
      prometheus:
        enabled: true
    web:
      server:
        request:
          autotime:
            enabled: true

logging:
  level:
    org.springframework.cloud.config: DEBUG
    org.springframework.web: INFO
  pattern:
    file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"

# Custom health indicators
health:
  config:
    git:
      timeout: 5s
  diskspace:
    threshold: 10MB

Docker Compose HA Example

# docker-compose.yml for HA Config Server
version: '3.8'
services:
  config-server-1:
    image: config-server:latest
    ports:
      - "8888:8888"
    environment:
      - SPRING_PROFILES_ACTIVE=ha,peer1
      - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://eureka:8761/eureka/
    depends_on:
      - eureka

  config-server-2:
    image: config-server:latest
    ports:
      - "8889:8888"
    environment:
      - SPRING_PROFILES_ACTIVE=ha,peer2
      - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://eureka:8761/eureka/
    depends_on:
      - eureka

  nginx-lb:
    image: nginx:alpine
    ports:
      - "8890:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - config-server-1
      - config-server-2

Lesson Summary

In this lesson, we explored centralized configuration management with Spring Cloud Config Server for microservices architectures. Here's a comprehensive summary of all the concepts and implementation approaches covered:

Configuration Management Fundamentals

  • Local config problems: Configuration drift, coordination nightmares, security liabilities, and deployment complexity
  • Centralized benefits: Single source of truth, better security practices, and support for advanced deployment patterns
  • Core advantages: Consistency across services, dynamic refresh capabilities, and simplified environment management
  • Operational benefits: Better observability, audit trails, and version control integration

Spring Cloud Config Server

  • Purpose: Centralized configuration management with HTTP-based access for distributed Spring Boot applications
  • Git integration: Uses Git repositories for version control, audit trails, and branching support
  • Profile support: Environment-specific configurations (dev, test, prod) with automatic serving based on active profiles
  • Setup: @EnableConfigServer annotation with Git URI configuration and health monitoring

Git Repository Structure

  • Naming conventions: application.yml for global defaults, service-specific files, and environment overrides
  • Hierarchy: Global defaults, service-specific configs, and environment-specific overrides
  • Organization: Structured approach with clear separation between environments and services
  • Version control: Leverages Git's branching, merging, and rollback capabilities for configuration management

Config Clients

  • Integration: spring-cloud-starter-config dependency with bootstrap.yml configuration
  • Service discovery: Can discover Config Server through Eureka for high availability scenarios
  • Configuration priority: Service-specific properties override global ones, environment-specific override defaults
  • Startup behavior: Config fetched before Spring context initialization ensures all properties are available

Dynamic Configuration Refresh

  • @RefreshScope: Enables beans to be reinitialized with new configuration values during runtime
  • Refresh endpoint: /actuator/refresh triggers configuration update without application restart
  • Use cases: Feature flags, operational parameters, and business logic settings that need immediate updates
  • Limitations: Structural changes like server ports typically still require application restarts

Configuration Properties

  • @Value annotation: Simple property injection with default values and type conversion
  • @ConfigurationProperties: Type-safe configuration binding with validation and structure
  • Property hierarchy: Global application properties, service-specific overrides, and environment customizations
  • Validation: Built-in validation support for configuration properties with custom constraints

Security Implementation

  • Basic Authentication: Simple username/password protection suitable for internal networks
  • OAuth2 integration: Enterprise-grade token-based authentication with existing identity providers
  • JWT authentication: Stateless, scalable security with signed tokens containing identity and authorization
  • Property encryption: Symmetric and asymmetric encryption for sensitive data with {cipher} prefix

Encryption and Sensitive Data

  • Encryption keys: Symmetric keys for development, asymmetric keys for production security
  • Transparent decryption: Config Server or clients automatically decrypt {cipher} prefixed values
  • Security benefits: Sensitive data never stored in plain text in version control systems
  • Key management: Secure storage and rotation of encryption keys for compliance requirements

High Availability Setup

  • Multiple instances: Config Server instances across availability zones with identical configuration
  • Load balancing: Client-side with Spring Cloud LoadBalancer or server-side with NGINX/HAProxy
  • Service discovery: Eureka integration for automatic Config Server discovery and failover
  • Health monitoring: Git repository synchronization status and instance health tracking

Monitoring and Operations

  • Actuator integration: Health checks, metrics, and configuration refresh endpoints
  • Metrics collection: Response times, error rates, Git operations, and client connection patterns
  • Alerting: Critical events like server unavailability, Git access failures, and refresh errors
  • Log aggregation: Configuration usage trends, performance analysis, and audit trails

Production Deployment

  • Container orchestration: Kubernetes or Docker Swarm for automatic instance management
  • Git repository: Secure access with SSH keys or access tokens for production repositories
  • Environment separation: Different Config Server instances or profiles for different environments
  • Backup and disaster recovery: Git's distributed nature provides automatic backup and recovery capabilities

Key Takeaways

  • Centralized configuration management is essential for maintaining consistency and operational efficiency in microservices
  • Spring Cloud Config Server provides production-ready features with Git integration for version control and audit trails
  • Dynamic refresh capabilities enable real-time configuration updates for feature flags and operational parameters
  • Comprehensive security options from Basic Auth to OAuth2 and property encryption meet various compliance requirements
  • High availability through multiple instances and load balancing ensures configuration services remain operational during failures