Master Spring Boot Profiles: A Comprehensive Guide for Developers
Modern application development requires running code across distinct environments—such as local development, staging, and production. While the core application logic remains constant across environments, environment-specific configurations (such as database connection URLs, server ports, security policies, and credentials) must vary. Spring Boot Profiles provide a robust, declarative mechanism to segregate application configuration and conditionally activate components based on the target environment.
1. The Core Problem: Multi-Environment Deployments
During application development, code runs in at least two distinct operational environments:
- Development / Staging Environment: A sandbox environment where developers build features, run tests, and insert dummy data without affecting real users.
- Production Environment: The live server environment where actual users interact with real business data, requiring high security, optimal performance, and strict access controls.
Real-Life Analogy
Think of a development database as a sandbox where you can freely add test records like
"Ram","Shyam", or"Ghanshyam". If you pollute or wipe the development database, no real-world business operation is impacted.In contrast, the production environment contains actual user data. While developers may be granted read-only access to production logs or read replicas for troubleshooting, direct write access is restricted to prevent catastrophic data corruption.
Hardcoding environment-specific properties directly in code or maintaining separate codebases for different environments creates maintenance bottlenecks, deployment risks, and configuration drift.
2. What Are Spring Boot Profiles?
Spring Boot Profiles provide a way to segregate parts of your application configuration and make them available only in specific environments.
When an application runs without explicit configuration, Spring Boot activates the default profile named default. Custom profiles allow developers to define override layers that modify base settings depending on the active environment (e.g., dev, staging, prod, local).
3. Creating and Structuring Profile Files
Spring Boot recognizes profile-specific configuration files using the standard naming convention:
application-{profile}.ymlorapplication-{profile}.properties
Modular YAML Configuration Strategy
Rather than duplicating shared configurations across multiple files, keep common settings in the base application.yml file and define environment-specific overrides in dedicated profile files.
Base Configuration: src/main/resources/application.yml
spring:
data:
mongodb:
database: journaldb
auto-index-creation: true
Development Profile: src/main/resources/application-dev.yml
server:
port: 8080
spring:
data:
mongodb:
host: localhost
port: 27017
Production Profile: src/main/resources/application-prod.yml
server:
port: 8081
spring:
data:
mongodb:
host: prod.mongo.cluster.internal
port: 27017
username: prod_user
password: ${MONGO_PROD_PASSWORD}
Property Resolution Sequence
Spring Boot resolves configuration properties using a hierarchical overriding mechanism:
4. Activating Profiles Across Environments
Spring Boot provides multiple mechanisms to set active profiles, ranging from quick local overrides to secure production configurations.
Approach A: Declarative Configuration inside Base application.yml
You can declare the active profile directly inside application.yml:
spring:
profiles:
active: dev
Note: While convenient during initial local development, hardcoding
spring.profiles.activein version-controlled configuration files is discouraged for production workflows. Re-committing code just to change an active profile before deployment introduces risk.
Approach B: IDE Execution Settings (IntelliJ IDEA)
To run profiles locally without modifying configuration files, set environment variables or VM options within your IDE's Run/Debug Configurations.
In IntelliJ IDEA:
- Open Run/Debug Configurations -> Edit Configurations.
- Navigate to Environment Variables or Active Profiles.
- Add the key-value pair:
SPRING_PROFILES_ACTIVE=dev
Approach C: Executable JAR Command-Line Arguments
When executing a compiled standalone JAR file, pass the --spring.profiles.active argument directly to the command line:
# Launching with the Development profile
java -jar target/journalApp-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev
# Launching with the Production profile (Server runs on Port 8081)
java -jar target/journalApp-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod
5. Build Pipelines, Testing, and Maven Integration
When packaging Spring Boot applications via build automation tools such as Apache Maven (mvn clean package), integration tests execute inside a Spring ApplicationContext. If test runs rely on active profile configurations, the active profile must be supplied during compilation and test execution.
Passing JVM System Properties (-D Flag)
To activate a profile during Maven build lifecycle execution (including test execution), pass the property as a JVM System Property using the -D flag:
mvn clean package -Dspring.profiles.active=dev
Distinction Between JVM System Properties (-D) and Application Arguments (--)
CI/CD Pipeline Integration (e.g., Jenkins)
In automated enterprise environments, developers do not run terminal commands directly on production servers. Continuous Integration/Continuous Deployment (CI/CD) systems like Jenkins handle build and release steps.
6. Conditional Bean Creation using @Profile
Spring Profiles extend beyond property values; they can control which Spring Beans are instantiated inside the IoC (Inversion of Control) container using the @Profile annotation.
Scenario: Environment-Specific Security Configurations
Consider an application where security rules differ between environments:
- Development: Endpoints are open (
permitAll()) to facilitate rapid API testing via tools like Postman. - Production: All endpoints strictly require authentication (
authenticated()).
Development Security Configuration
package com.example.journalApp.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@Profile("dev")
public class DevSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().permitAll() // Relaxed permissions for dev
);
return http.build();
}
}
Production Security Configuration
package com.example.journalApp.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@Profile("prod")
public class ProdSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/health").permitAll()
.anyRequest().authenticated() // Strict authentication enforced
);
return http.build();
}
}
Runtime Behavior Verification
When calling an endpoint such as GET http://localhost:8081/journal without credentials:
7. Programmatic Profile Access & Testing with @ActiveProfiles
Programmatic Inspection via Environment Bean
You can inspect active profiles at runtime by injecting Spring's Environment interface:
package com.example.journalApp.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import jakarta.annotation.PostConstruct;
@Service
public class EnvironmentService {
@Autowired
private Environment environment;
@PostConstruct
public void printActiveProfile() {
String[] activeProfiles = environment.getActiveProfiles();
if (activeProfiles.length > 0) {
System.out.println("Currently Active Profile: " + activeProfiles[0]);
} else {
System.out.println("No active profile set. Falling back to default.");
}
}
}
Note:
environment.getActiveProfiles()returns an array (String[]) because Spring Boot supports multiple concurrent active profiles.
Integration Testing using @ActiveProfiles
To enforce a specific profile context during JUnit testing, use the @ActiveProfiles test annotation:
package com.example.journalApp.service;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
@SpringBootTest
@ActiveProfiles("dev")
public class UserServiceTest {
@Test
void testUserCreationInDevContext() {
// Test executes guaranteed under the 'dev' profile context
}
}
8. Multiple Active Profiles, IP Whitelisting, and Configuration Externalization
1. Activating Multiple Concurrent Profiles
Spring Boot supports activating multiple profiles simultaneously by providing a comma-separated list:
java -jar journalApp.jar --spring.profiles.active=dev,db
2. Infrastructure Security & IP Whitelisting
In production architectures, sensitive resources like production database clusters are secured behind IP Whitelists.
Local Developer Machine (IP: 192.168.1.10)
|
X Blocked (Connection Refused - IP Not Whitelisted)
v
Production Database Cluster (Mongo DB Port 27017)
^
| Allowed (Whitelisted Network Interface)
|
Production Application Instance (IP: 10.0.0.5)
Because local developer machines are not whitelisted to access production databases, attempting to run --spring.profiles.active=prod from a local environment will fail at the network connection layer.
3. Externalizing Sensitive Properties
To avoid storing production secrets or credentials in version control repositories, Spring Boot allows externalizing configuration files outside the application JAR file:
java -jar journalApp.jar --spring.config.location=file:/var/config/journalApp/application-prod.yml
Summary and Key Takeaways
- Environment Separation: Spring Boot Profiles allow separating configurations (ports, credentials, database connection strings) across environments like development, staging, and production without changing application code.
- Profile File Naming: Create environment-specific files following the
application-{profile}.ymlor.propertiespattern (e.g.,application-dev.yml,application-prod.yml). - Base Configuration Fallback: Shared/common properties belong in
application.yml. Profile-specific configuration files override base properties when activated. - IDE Activation: Set
SPRING_PROFILES_ACTIVE=devin IntelliJ IDEA's Run Configurations to test profiles locally. - Executable JAR Activation: Pass application arguments at launch using
--spring.profiles.active=prod. - Maven Build and Test Activation: Use JVM System Properties (
-Dspring.profiles.active=dev) withmvn clean packageso test executions trigger the intended Spring application context. - Conditional Bean Loading: Use
@Profile("dev")or@Profile("prod")on@Configurationor@Componentclasses to conditionally register beans (e.g., security filters, mail services). - Programmatic Inspection: Inject the
Environmentbean and callenvironment.getActiveProfiles()to inspect activated profiles programmatically. - Test Context Control: Annotate integration test classes with
@ActiveProfiles("dev")to bind test execution to a specific profile environment.