A Complete Guide to Multi-Environment Configuration in Spring Boot

In real-world development and deployment, a single project often needs to run in multiple environments (e.g., local, dev, staging, production).
Spring Boot provides a powerful mechanism for environment-based configuration, allowing us to automatically load different settings based on runtime environment variables.

This article walks through how to design a clean and maintainable multi-environment configuration structure step by step.

1. Design Goal

The following behavior is desired:

Environment Variable $ENV Configuration File Used
Not Set / None application-local.yml
production application-production.yml
staging application-staging.yml
dev application-dev.yml

src/
└── main/
    └── resources/
        ├── application.yml
        ├── application-local.yml
        ├── application-dev.yml
        ├── application-staging.yml
        └── application-production.yml

3. Main Configuration: application.yml

In the main configuration, define the default profile as ${ENV:local}:

spring:
  profiles:
    active: ${ENV:local}  # Default to local if ENV is not set

ENV is a system environment variable. ${ENV:local} means if it’s not set, the default profile is local.


4. Example Configuration Files

application-local.yml

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/app_local
    username: root
    password: root

application-production.yml

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://prod-db:3306/app_prod
    username: prod_user
    password: ${DB_PASSWORD}

application-staging.yml

server:
  port: 8081

spring:
  datasource:
    url: jdbc:mysql://staging-db:3306/app_staging
    username: staging_user
    password: staging_pwd

5. Switching Environments at Runtime

Option 1: Using Environment Variable

export ENV=production
java -jar app.jar

Or simply:

ENV=production java -jar app.jar

Option 2: Using Spring Boot Property

java -jar app.jar --spring.profiles.active=production

6. Reading the Active Profile in Code

If you need to dynamically read the active profile inside the application:

import org.springframework.core.env.Environment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class EnvChecker {

    @Autowired
    private Environment env;

    public void printActiveProfile() {
        String[] profiles = env.getActiveProfiles();
        if (profiles.length == 0) {
            System.out.println("No active profile set, using default: local");
        } else {
            System.out.println("Active profile: " + String.join(", ", profiles));
        }
    }
}

7. Loading Different Beans by Profile (Optional)

You can use @Profile to load environment-specific beans:

@Configuration
@Profile("production")
public class ProductionConfig {
    @Bean
    public MyService myService() {
        return new ProdMyService();
    }
}
@Configuration
@Profile("local")
public class LocalConfig {
    @Bean
    public MyService myService() {
        return new LocalMyService();
    }
}

8. Packaging Strategy: One JAR for All Environments

Build Phase

When executing:

mvn clean package -DskipTests

or

./gradlew build

Spring Boot includes all configuration files (application*.yml) in the JAR package.
At this stage, ${ENV} is not parsed and no profile is activated.

Resulting structure inside the JAR:

BOOT-INF/classes/
 ├── application.yml
 ├── application-local.yml
 ├── application-production.yml
 ├── application-staging.yml
 └── application-dev.yml

Runtime Phase

The environment variable determines which configuration file to load:

export ENV=production
java -jar app.jar

The same JAR can run in different environments without rebuilding.


9. Docker Example

Dockerfile:

FROM eclipse-temurin:17-jdk
WORKDIR /app
COPY target/app.jar .
ENV ENV=production
ENTRYPOINT ["java", "-jar", "app.jar"]

Override the environment variable at runtime if needed:

docker run -e ENV=staging myapp:latest

10. Deployment Strategy Recommendation

Environment Deployment Method ENV Variable
Local Manual Run ENV=local
Dev CI/CD Pipeline ENV=dev
Staging CI/CD Pipeline ENV=staging
Production CI/CD / Docker ENV=production

Summary

Feature Description
Default Profile Defined in application.yml as ${ENV:local}
Multi-Environment Files application-{profile}.yml
Packaging One JAR for all environments
Runtime Switching ENV=production java -jar app.jar
Reading Active Profile Environment.getActiveProfiles()
Environment-Specific Beans Use @Profile("xxx")

Key Takeaway:
Spring Boot’s profile mechanism allows one universal build that can adapt to any environment simply by setting an environment variable — clean, consistent, and deployment-friendly.