Config Profiles and Environment-Specific Configuration
Fast recall
Quick revision
Core answer
Whether you can design a configuration structure that avoids duplication (DRY), is safe for production (no plaintext secrets in Git), and supports multiple environments without requiring code changes
Putting environment-specific properties inside the application code as @Profile @Configuration classes — this couples environment differences to the deployable artifact and means rebuilding to change a config value
Describe using Spring Boot Actuator's /actuator/env endpoint to verify which configuration values are active at runtime, explain the Config Server's property override feature (spring.cloud.config.server.overrides) for emergency values, and discuss using CI/CD to trigger git commits to the config repo as part of a deployment pipeline
Interview-ready guide
Interview answer
Spring Cloud Config resolves properties in a specific order that most developers get wrong. App-specific files always beat application-wide files. order-service.yml overrides application-prod.yml. Only order-service-prod.yml overrides order-service.yml.
The pattern: shared defaults in application.yml, service-specific overrides in {appname}.yml, environment-specific values in {appname}-{profile}.yml.
Multiple profiles can be active simultaneously — prod,ssl loads both. Last profile wins on conflicts.
Labels map to Git branches — test config changes on a branch before merging.
Never store secrets in profile-specific config files in Git — use Vault or cloud secret managers. Store only non-sensitive values like hostnames and feature flags.
For local development, spring.config.import=optional:configserver: makes the Config Server optional — the app starts with local properties if the server is down.
In practice, I use three levels: shared defaults, app-specific config, and environment-specific overrides, with secrets always in Vault.
Config Profiles and Environment-Specific Configuration
Direct Answer
Spring profiles drive environment-specific configuration in Spring Cloud Config: the active profile (dev, staging, prod) determines which files the Config Server serves to a client. File resolution is hierarchical — application.yml sets shared defaults, {app}-{profile}.yml overrides for specific environments. Secret values are encrypted in Git or delegated to Vault.
Code Example
The repo structure for a Config Server's Git backend, with realistic content per file, and how each piece gets activated.
Directory layout (flat is simplest):
config-repo/
├── application.yml # shared defaults — every service, every env
├── application-prod.yml # shared prod overrides
├── application-staging.yml # shared staging overrides
├── order-service.yml # order defaults — all envs
├── order-service-dev.yml # order + dev
├── order-service-staging.yml # order + staging
├── order-service-prod.yml # order + prod (highest precedence)
├── payment-service.yml
├── payment-service-prod.yml
└── search-service.yml
application.yml — org-wide defaults:
logging:
pattern:
level: "%5p [${spring.application.name:-},%X{traceId:-},%X{spanId:-}]"
management:
endpoints.web.exposure.include: health,info,metrics,prometheus,env,configprops
endpoint.health.show-details: when-authorized
spring:
jackson.default-property-inclusion: NON_NULL
application-prod.yml — shared prod overrides:
management:
endpoints.web.exposure.include: health,info,metrics,prometheus # no /env in prod
tracing.sampling.probability: 0.1 # 10% trace sampling
logging.level.root: WARN
order-service.yml — service defaults:
spring:
datasource:
hikari:
maximum-pool-size: 10
minimum-idle: 2
jpa.hibernate.ddl-auto: validate
resilience4j:
circuitbreaker.instances.payment-svc:
failureRateThreshold: 50
slidingWindowSize: 20
features:
new-checkout-flow: false # off everywhere by default
order-service-prod.yml — env + service specific:
spring:
datasource:
url: jdbc:postgresql://orders-prod.db.internal:5432/orders
username: orders_app
# password via Vault — NOT in Git
hikari.maximum-pool-size: 30
payment-service:
base-url: https://payments.prod.internal
features:
new-checkout-flow: true # on in prod
order-service-dev.yml — env + service specific:
spring:
datasource:
url: jdbc:h2:mem:orders;DB_CLOSE_DELAY=-1
username: sa
password: sa
payment-service:
base-url: http://localhost:9090 # local payment-service
logging.level.com.example.order: DEBUG
Config Server — serving this repo:
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApp {
public static void main(String[] args) { SpringApplication.run(ConfigServerApp.class, args); }
}
# config-server application.yml
server.port: 8888
spring:
cloud:
config:
server:
git:
uri: git@github.com:example/config-repo.git
search-paths: ''
clone-on-start: true
default-label: main
Client — order-service activates profile via environment:
# order-service bootstrap or application.yml
spring:
application.name: order-service
config.import: optional:configserver:http://config-server:8888
profiles.active: ${ENV_PROFILE:dev} # externalized — set by k8s/compose
cloud.config:
fail-fast: true
retry:
initial-interval: 1000
max-attempts: 6
multiplier: 1.5
label: ${CONFIG_LABEL:main}
Kubernetes deployment sets the profile:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
template:
spec:
containers:
- name: order-service
image: example/order-service:2.3.1
env:
- name: ENV_PROFILE
value: prod
- name: CONFIG_LABEL
value: release/2026.04
What order-service sees at runtime (profile=prod):
Config Server merges in this precedence (later overrides earlier):
application.ymlapplication-prod.ymlorder-service.ymlorder-service-prod.yml← wins on conflicts
Verify at runtime:
curl http://order-service:8080/actuator/env/features.new-checkout-flow
# shows the source chain:
# { "property": {"value": true, "origin": "config-repo/order-service-prod.yml"},
# "propertySources": [
# {"name":"configserver:config-repo/order-service-prod.yml", "property":{...true}},
# {"name":"configserver:config-repo/order-service.yml", "property":{...false}}
# ]}
Feature-flag profile composition — activate a flag-scoped profile:
env:
- name: SPRING_PROFILES_ACTIVE
value: prod,canary-2026q2
Config Server additionally merges order-service-canary-2026q2.yml with higher precedence than the prod file. Turning the canary off is removing the env value and redeploying — no code change.
Common Mistakes & Pitfalls
Putting environment-specific properties inside the application code as @Profile @Configuration classes — this couples environment differences to the deployable artifact and means rebuilding to change a config value
Check your understanding
Answer these without looking back at the article.
- What is the order of precedence when the same property is defined in multiple Spring profile configuration files?
- How do you prevent accidental deployment of development configuration to production?
- How would you handle rotating a database password stored in Spring Cloud Config without downtime?
Related interview prep categories
2Want a guided curriculum order?
These modules are also sequenced in our flagship backend interview track — open it when you prefer an ordered path instead of browsing by topic.