Service Discovery, Config & Gateway

Centralized Configuration

18 min Lesson 4 of 12

Centralized Configuration

In a monolith you manage one application.properties file. Deploy ten microservices across three environments and you suddenly have thirty configuration files to keep consistent. Change a shared database URL and you must update, rebuild, and redeploy every service. Spring Cloud Config solves this by introducing a dedicated Config Server that acts as the single source of truth for all configuration, serving properties to every microservice over HTTP at startup — and optionally at runtime without a restart.

How Spring Cloud Config Works

The architecture has two moving parts:

  • Config Server — a standalone Spring Boot application that reads configuration from a backend store (typically a Git repository) and exposes it via a REST API.
  • Config Client — any microservice that has the spring-cloud-starter-config dependency. It contacts the Config Server during startup, downloads its properties, and merges them into its Environment before any beans are created.
Why Git as the backend? Storing configuration in Git gives you version history, branch-per-environment workflows, pull-request reviews for config changes, and an audit trail — all for free. The Config Server can also read from the local filesystem, Vault, JDBC, and other backends, but Git is the production default.

Setting Up the Config Server

Create a new Spring Boot project and add the server dependency:

<!-- pom.xml --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>

Enable the server with a single annotation on the main class:

package com.example.configserver; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } }

Configure where it reads properties from in its own application.yml:

server: port: 8888 spring: cloud: config: server: git: uri: https://github.com/your-org/service-configs default-label: main # Git branch to use search-paths: '{application}' # look in a sub-folder named after the service clone-on-start: true # fail fast if the repo is unreachable

The server now serves configuration at the URL pattern /{application}/{profile}/{label}. For example, GET /order-service/production/main returns the merged properties for the order-service application in the production profile on the main branch.

Organising the Config Repository

A typical Git config repo layout looks like this:

service-configs/ application.yml # shared by ALL services application-production.yml # shared overrides for production order-service/ application.yml # order-service defaults application-production.yml inventory-service/ application.yml application-production.yml

Spring Cloud Config merges these files with a clear precedence (most specific wins):

  1. Service-specific + profile (e.g. order-service/application-production.yml)
  2. Service-specific default (e.g. order-service/application.yml)
  3. Shared + profile (e.g. application-production.yml)
  4. Shared default (application.yml)
Put truly shared values at the top. Database driver class names, common timeouts, and shared feature flags belong in the root application.yml. Service-specific URLs and credentials belong in per-service files. This keeps individual service configs small and focused.

Configuring a Config Client

In each microservice, replace the default Spring Boot config starter with the Config Client:

<!-- pom.xml of the microservice --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>

Add the minimal bootstrap configuration. In Spring Boot 3 / Spring Cloud 2022+, bootstrap processing is disabled by default; re-enable it with the spring-cloud-starter-bootstrap dependency, or — the preferred modern approach — put Config Server coordinates directly in application.yml:

# order-service/src/main/resources/application.yml spring: application: name: order-service # used by Config Server to find your files config: import: "configserver:http://localhost:8888" cloud: config: profile: ${SPRING_PROFILES_ACTIVE:development} fail-fast: true # crash on startup if Config Server is unreachable retry: max-attempts: 6 initial-interval: 1000 # 1 s between retries

When the application starts, Spring Boot processes the spring.config.import entry before any other beans are created, fetches the remote properties, and injects them into the application context exactly as if they were local .yml files.

Securing the Config Server

The Config Server can expose database passwords, API keys, and other secrets. Protect it properly:

  • HTTP Basic / OAuth2: Add spring-boot-starter-security to the Config Server and configure credentials. Clients supply them in the spring.cloud.config.username and spring.cloud.config.password properties (stored in a local secret manager, not in the repo).
  • Symmetric encryption: The Config Server has built-in support for encrypting values with a shared secret. Store the encrypted value in Git as {cipher}AQA... — the server decrypts it before serving. Set ENCRYPT_KEY as an environment variable on the server host, never in config files.
  • Asymmetric encryption: Use a RSA key pair stored in a JKS keystore for higher security. The server holds the private key; the Git repo contains only ciphertext.
# Encrypting a value with the Config Server's built-in endpoint: # POST http://config-server:8888/encrypt # Body: s3cr3t-db-password # # Response: AQBxyz123... # # Store in Git as: spring: datasource: password: '{cipher}AQBxyz123...'
Never store plaintext secrets in the Git config repo. Anyone with read access to the repository — now or in its commit history — can extract them. Encrypt every secret before committing. Rotate any key that was ever committed in plaintext immediately.

Network Topology in a Real Cluster

In production the Config Server itself runs as a service registered with Eureka (covered in Lesson 2). Clients discover it by service ID rather than a hardcoded URL:

spring: cloud: config: discovery: enabled: true service-id: config-server # Eureka service ID

This removes the last hardcoded URL from your clients. The startup sequence becomes: contact Eureka → discover Config Server → fetch config → finish starting up. The tradeoff is that Eureka must be reachable before any other service can start; this is why Eureka itself is typically deployed with multiple replicas and a fixed, well-known address.

Summary

Spring Cloud Config replaces the per-service application.yml chaos with a single Git-backed server that serves versioned, environment-aware, profile-specific configuration to every microservice. Clients connect at startup via spring.config.import, the server merges properties by precedence, and sensitive values can be encrypted at rest. In the next lesson you will see how to push updated configuration to running services without restarting them.