Spring Cloud Config + Docker: The Professional Way to Configure Microservices
One of the most common challenges when building Spring Boot microservices is making the same application work seamlessly in different environments. For example: You run the application from…
One of the most common challenges when building Spring Boot microservices is making the same application work seamlessly in different environments.
For example:
- You run the application from IntelliJ on your local machine.
- Later, you deploy the same application inside Docker.
- Eventually, you may deploy it to Kubernetes or a cloud platform.
The problem is that each environment has different ways of reaching the Config Server.
If you hardcode one URL, another environment will fail.
Many beginners solve this by maintaining multiple configuration files or changing the source code before deployment.
Professional teams don’t do that.
Instead, they let Spring Boot’s configuration hierarchy handle everything.
In this article, you’ll learn:
- Why
localhostworks locally but not inside Docker - How Docker overrides
application.yml - How Spring Boot decides which property to use
- Why environment variables have higher priority
- How Docker networking works internally
- The complete execution flow
- Interview questions and answers
By the end, you’ll understand how one JAR file can run perfectly in both local and Docker environments without changing a single line of code.
The Problem
Suppose you have the following architecture.
Client Microservice
│
│
â–¼
Spring Cloud Config Server
│
â–¼
Git Repository
Every microservice needs to contact the Config Server during startup to fetch its configuration.
But the Config Server address changes depending on where the application runs.
Local Development
Config Server
http://localhost:8071
Docker
Config Server
http://configserver:8071
Why are they different?
Let’s understand.
Running the Application Locally
Suppose you start the Config Server directly from IntelliJ.
Config Server
Running on your laptop
Port 8071
Now your Accounts service also runs from IntelliJ.
Both applications are running on the same operating system.
+--------------------------------+
Your Laptop
Accounts Service
Config Server
+--------------------------------+
Since both applications are running on the same machine, they communicate using localhost.
Your application.yml looks like this:
spring:
application:
name: accounts
config:
import: "configserver:http://localhost:8071"
server:
port: 8080
During startup, Spring Boot reads:
configserver:http://localhost:8071
Then it contacts the Config Server.
Accounts Service
│
â–¼
localhost:8071
│
â–¼
Config Server
│
â–¼
Git Repository
Everything works perfectly.
Why Doesn’t localhost Work Inside Docker?
Now imagine every application is running inside its own container.
+--------------------+
| Accounts Container |
+--------------------+
+--------------------+
| Config Container |
+--------------------+
Many developers assume:
localhost:8071
will still work.
It won’t.
This is because localhost always refers to the current machine or container.
Inside the Accounts container,
localhost
means
Accounts Container
NOT
Config Server Container
So Spring Boot tries to find the Config Server inside the Accounts container itself.
There isn’t one.
The result:
Connection Refused
or
UnknownHostException
depending on the configuration.
How Docker Solves This Problem
Docker Compose automatically creates an internal network.
For example,
services:
configserver:
accounts:
loans:
cards:
Docker creates something similar to:
Internal Docker Network
Every service name automatically becomes a DNS hostname.
configserver
accounts
cards
loans
Now the Accounts service can simply call
http://configserver:8071
Docker internally converts
configserver
into the Config Server container’s IP address.
No IP address needs to be written manually.
Why Not Put configserver in application.yml?
Some beginners write:
spring:
config:
import: "configserver:http://configserver:8071"
Looks good.
Until they run the application locally.
Spring Boot now tries to resolve
configserver
through the operating system.
Windows or macOS says:
Who is configserver?
There is no such hostname.
Because Docker isn’t running the application.
The result:
java.net.UnknownHostException: configserver
This is why we keep
localhost
inside the source code.
The Professional Solution
Instead of modifying the source code for Docker, Docker overrides the property using an environment variable.
Your application.yml remains:
spring:
application:
name: accounts
config:
import: "configserver:http://localhost:8071"
Now create a common Docker Compose configuration.
services:
microservice-configserver-config:
environment:
SPRING_CONFIG_IMPORT: "configserver:http://configserver:8071"
SPRING_PROFILES_ACTIVE: "default"
Then every service extends it.
accounts:
extends:
file: common-config.yml
service: microservice-configserver-config
environment:
SPRING_APPLICATION_NAME: "accounts"
Notice something interesting.
We never changed
application.yml
Instead,
Docker simply injects
SPRING_CONFIG_IMPORT
during container startup.
How Does Spring Boot Know Which Value to Use?
Spring Boot loads configuration from multiple sources.
Each source has a priority.
Higher-priority sources override lower-priority ones.
A simplified priority order is:
| Priority | Configuration Source |
|---|---|
| Highest | Command-line arguments |
| 2 | Java System Properties |
| 3 | Environment Variables |
| 4 | application.properties / application.yml |
| Lowest | Default values inside code |
Because Docker sets
SPRING_CONFIG_IMPORT
as an environment variable,
Spring Boot ignores the value from
application.yml
and uses the Docker value instead.
No code changes.
No rebuild.
No separate JAR.
Complete Startup Flow (Local)
application.yml
│
â–¼
configserver:http://localhost:8071
│
â–¼
Spring Boot
│
â–¼
Config Server
│
â–¼
Git Repository
Complete Startup Flow (Docker)
application.yml
│
â–¼
localhost:8071
│
â–¼
Docker Environment Variable
SPRING_CONFIG_IMPORT
=configserver:http://configserver:8071
│
â–¼
Overrides application.yml
│
â–¼
Spring Boot
│
â–¼
configserver:8071
│
â–¼
Docker DNS
│
â–¼
Config Server Container
│
â–¼
Git Repository
What Happens Internally During Startup?
Let’s follow the sequence step by step.
Step 1
Docker starts the Accounts container.
docker-compose up
↓
Step 2
Docker injects environment variables.
SPRING_CONFIG_IMPORT
SPRING_APPLICATION_NAME
SPRING_PROFILES_ACTIVE
↓
Step 3
Spring Boot starts.
↓
Step 4
Spring Boot reads all property sources.
application.yml
Environment Variables
System Properties
Command Line
↓
Step 5
Spring Boot merges all configurations.
↓
Step 6
Environment variables override duplicate keys.
application.yml
↓
SPRING_CONFIG_IMPORT
wins
↓
Step 7
Spring Boot contacts
configserver:8071
↓
Step 8
Docker DNS resolves
configserver
↓
Step 9
The Config Server returns configuration from the Git repository.
↓
Step 10
The Accounts service starts successfully.
Why Use a Common Docker Configuration?
Instead of repeating the same environment variables in every service, create a reusable configuration.
common-config.yml
Then each service extends it.
Benefits:
- No duplication
- Easier maintenance
- Consistent configuration across all services
- Easier to add new microservices
- Single place to modify common settings
This follows the DRY (Don’t Repeat Yourself) principle.
Common Mistakes
Mistake 1
Using
configserver
inside application.yml.
Result:
UnknownHostException
when running locally.
Mistake 2
Using
localhost
inside Docker.
Result:
Connection refused
because localhost refers to the current container.
Mistake 3
Creating different source code for Docker and local execution.
Result:
- Multiple configuration files
- Maintenance headaches
- Deployment errors
Mistake 4
Building separate JAR files for different environments.
This defeats the purpose of externalized configuration.
A single application artifact should work everywhere.
Best Practices
- Keep
application.ymlsuitable for local development. - Override environment-specific values using environment variables.
- Use Docker service names for inter-container communication.
- Store shared Docker configuration in a common Compose file.
- Never hardcode environment-specific values into application code.
- Build once, deploy everywhere.
Interview Questions and Answers
Q1. How do you configure Spring Cloud Config to work both locally and in Docker?
Answer:
“In my client microservices, I configure the Config Server URL as
http://localhost:8071inapplication.ymlso the application works when running from IntelliJ or any local environment. For Docker deployments, I don’t change the source code. Instead, I override the property using the environment variableSPRING_CONFIG_IMPORT=configserver:http://configserver:8071. Spring Boot gives higher priority to environment variables thanapplication.yml, so the Docker value automatically replaces the local value. This allows the same JAR or Docker image to work in both environments without any code changes.”
Q2. Why doesn’t localhost work inside Docker?
Answer:
Because localhost always points to the current container. If the Accounts service uses localhost:8071, it tries to connect to port 8071 within its own container instead of the Config Server container.
Q3. Why does configserver work inside Docker?
Answer:
Docker Compose creates an internal network and automatically registers each service name as a DNS hostname. Therefore, configserver resolves to the IP address of the Config Server container, allowing other containers to communicate using the service name.
Q4. Why not keep configserver in application.yml?
Answer:
Because when running the application directly from the IDE, the operating system has no knowledge of the Docker service name configserver. This results in an UnknownHostException. Using localhost for local development and overriding it in Docker avoids this issue.
Q5. Why are environment variables preferred for environment-specific configuration?
Answer:
Environment variables externalize configuration from the application. They allow the same application artifact to run across development, testing, staging, and production environments without rebuilding or modifying the source code. Spring Boot’s property precedence ensures they override values defined in configuration files.
Key Takeaways
localhostworks only when the client and Config Server run on the same machine.- Inside Docker,
localhostrefers to the current container, not another service. - Docker Compose automatically exposes service names as DNS hostnames.
- Spring Boot gives environment variables higher priority than
application.yml. SPRING_CONFIG_IMPORToverrides the Config Server URL at runtime.- A single JAR or Docker image can be reused across local, Docker, and cloud environments without code changes.
- Centralizing shared Docker settings in a common Compose file improves maintainability and follows the DRY principle.
By understanding Spring Boot’s configuration precedence and Docker’s networking model, you can design microservices that are portable, maintainable, and production-ready—using the same codebase and the same application artifact in every environment.