Contents

The Config Server acts as an HTTP endpoint that serves properties files pulled from a Git repository. Each client service fetches its configuration by providing its application name, active profile, and label (branch/tag/commit).

The Config Server is stateless — it simply proxies properties from the backing Git repo. Scale it horizontally behind a load balancer and point all services at the same URL. The Git clone is cached locally per server instance.

Add the following to your pom.xml (Maven) or build.gradle (Gradle). Spring Boot manages compatible versions automatically when you use the Spring Boot BOM.

<!-- pom.xml — Config Server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> @SpringBootApplication @EnableConfigServer // single annotation turns on the Config Server public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } } # application.yml — Config Server server: port: 8888 spring: application: name: config-server cloud: config: server: git: uri: https://github.com/my-org/config-repo # or git@github.com:... default-label: main # branch to use clone-on-start: true # eager clone at startup search-paths: # subdirectories to search - '{application}' # per-app subfolder # For private repos: username: ${GIT_USERNAME} password: ${GIT_TOKEN} # Or SSH key: # private-key: ${GIT_PRIVATE_KEY} management: endpoints: web: exposure: include: health,info,refresh,bus-refresh

Name files using the pattern {application}-{profile}.yml. Files are resolved in priority order: most specific first, then falling back to more generic.

config-repo/ ├── application.yml # shared defaults for ALL services ├── application-prod.yml # shared prod overrides for ALL services │ ├── order-service.yml # order-service defaults (all profiles) ├── order-service-dev.yml # order-service dev overrides ├── order-service-prod.yml # order-service prod overrides │ ├── payment-service.yml ├── payment-service-prod.yml │ └── inventory-service.yml # application.yml — shared defaults for all services logging: level: root: INFO management: endpoints: web: exposure: include: health,info --- # order-service-prod.yml spring: datasource: url: jdbc:postgresql://prod-db.internal:5432/orders username: orders_app password: '{cipher}AQABAAEAgBJ...' # encrypted value app: order: timeout-seconds: 30 max-retries: 3

Verify the server is serving config correctly by hitting the endpoint directly:

# GET /{application}/{profile}/{label} curl http://localhost:8888/order-service/prod/main curl http://localhost:8888/order-service/dev # Response includes all resolved property sources in priority order

Add the following to your pom.xml (Maven) or build.gradle (Gradle). Spring Boot manages compatible versions automatically when you use the Spring Boot BOM.

<!-- pom.xml — Config Client (each microservice) --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> # application.yml — Config Client spring: application: name: order-service # determines which files are fetched from config repo config: import: "optional:configserver:http://localhost:8888" # "optional:" means app starts even if server is down profiles: active: dev cloud: config: fail-fast: true # fail startup if config server unreachable (prod) retry: max-attempts: 6 initial-interval: 1000 multiplier: 1.5 max-interval: 2000 // Properties fetched from Config Server are injected normally @Service public class OrderService { @Value("${app.order.timeout-seconds:10}") private int timeoutSeconds; @Value("${app.order.max-retries:3}") private int maxRetries; } // Or use @ConfigurationProperties for type-safe binding @ConfigurationProperties(prefix = "app.order") @Component public class OrderProperties { private int timeoutSeconds = 10; private int maxRetries = 3; // getters/setters }

The YAML below shows the complete configuration for this feature. Adjust the values to match your environment.

# Priority order (highest to lowest) for order-service with profile=prod, label=main: # 1. order-service-prod.yml (app + profile specific) # 2. order-service.yml (app specific, all profiles) # 3. application-prod.yml (shared prod overrides) # 4. application.yml (shared defaults) # Labels — serve config from a specific branch or tag spring: cloud: config: label: release/v2.1 # fetch from this Git branch/tag # Config Server also serves native file format (e.g., for feature branches) # Composite backend — Git for prod, filesystem for local dev spring: cloud: config: server: composite: - type: git uri: https://github.com/my-org/config-repo - type: native search-locations: file:./local-config

Without restart, services can reload config by calling the /actuator/refresh endpoint (or using Spring Cloud Bus for fan-out). Beans annotated with @RefreshScope are re-created on refresh.

// Mark the bean as refreshable @RefreshScope @Service public class FeatureFlagService { @Value("${feature.new-checkout-enabled:false}") private boolean newCheckoutEnabled; public boolean isNewCheckoutEnabled() { return newCheckoutEnabled; // re-injected after refresh } } # Trigger a refresh on one service instance curl -X POST http://order-service:8080/actuator/refresh # Fan-out refresh to ALL instances via Spring Cloud Bus (Kafka or RabbitMQ) # Add: spring-cloud-starter-bus-kafka to all services curl -X POST http://config-server:8888/actuator/bus-refresh # Or refresh only services with a specific destination curl -X POST "http://config-server:8888/actuator/bus-refresh/order-service:**" @RefreshScope does NOT work with @ConfigurationProperties beans directly — those need @RefreshScope on the class OR you can use Spring Cloud's auto-rebinding by adding spring-cloud-context which auto-refreshes @ConfigurationProperties beans.

Config Server has built-in encryption/decryption. Store ciphered values in Git — Config Server decrypts them before serving to clients.

# Config Server application.yml — set the encryption key encrypt: key: my-symmetric-key # or use asymmetric RSA key pair # For RSA (more secure): # key-store: # location: classpath:keystore.jks # password: ${KEYSTORE_PASSWORD} # alias: config-server-key # Encrypt a value curl -X POST http://localhost:8888/encrypt -d 'my-secret-password' # Returns: AQABAAEAgBJ3xFq7... # Decrypt (for verification only — clients never call this) curl -X POST http://localhost:8888/decrypt -d 'AQABAAEAgBJ3xFq7...' # Returns: my-secret-password # Store in Git config file with {cipher} prefix spring: datasource: password: '{cipher}AQABAAEAgBJ3xFq7...' # Config Server decrypts this before sending to the client # Client receives the plaintext value For production use, prefer HashiCorp Vault as the Config Server backend instead of Git-based encryption. Spring Cloud Vault integrates natively and provides dynamic secrets, lease rotation, and fine-grained access policies.

The YAML below shows the complete configuration for this feature. Adjust the values to match your environment.

# Config Server — add Spring Security HTTP Basic auth # pom.xml: spring-boot-starter-security spring: security: user: name: config-admin password: ${CONFIG_SERVER_PASSWORD} # Config Client — provide credentials spring: cloud: config: uri: http://localhost:8888 username: config-admin password: ${CONFIG_SERVER_PASSWORD} # Production pattern — put Config Server behind service mesh or API gateway # and use mutual TLS or OAuth2 token-based auth instead of HTTP Basic spring: cloud: config: tls: enabled: true key-store: classpath:client-keystore.p12 key-store-password: ${TLS_PASSWORD} trust-store: classpath:truststore.p12 trust-store-password: ${TLS_TRUST_PASSWORD}