For DevelopersMarch 27, 2026

4 Ways to Configure Environment Variables in Spring Boot

Learn 4 ways to configure environment variables in Spring Boot for better security, scalability, and flexibility.

Environment variables are essential in modern app development because they let you keep configuration separate from your code. If you're using Spring Boot—a popular Java framework—you’ll appreciate how easily it handles environment variables for externalized configurations. 

This makes your applications more portable, scalable, and secure. In this guide, we’ll walk through how you can set up and use environment variables in Spring Boot, along with practical solutions and best practices.

🚀 Join Index.dev and get matched with top global companies for high-paying remote Java roles!

 

Core Concepts

Spring Boot implements a hierarchical property resolution system that determines how different configuration sources override each other. Understanding this hierarchy enables you to manage configurations effectively, ensuring consistency and adaptability across diverse environments.

  1. Command-line arguments
  2. JNDI attributes from java:comp/env
  3. JVM system properties
  4. OS environment variables
  5. Profile-specific properties
  6. Application properties/YAML files

Together, let us explore some methods for configuration management, while also addressing specific challenges and solutions that you might face in real-world scenarios.

Read More: How to Identify and Optimize Long-Running Queries in Java

 

Method 1: Type-safe Configuration Properties

Introduction

Enterprise applications need reliable, validated configuration management that catches errors at startup rather than runtime.

Problem Tackled

  • Loose typing with @Value annotations leads to runtime errors
  • No validation at startup
  • Mutable state in configuration objects
  • Difficult testing and maintenance

Solution

Implement strongly-typed configuration classes using @ConfigurationProperties with validation and immutability, following the Spring Boot best practices for externalized configuration.

Code Example

@Validated
@Configuration
@ConfigurationProperties(prefix = "app")
public class ApplicationConfig {
    private final DatabaseConfig database;
    private final SecurityConfig security;
    private final Map<String, String> features;
    
    @ConstructorBinding
    public ApplicationConfig(
            @NotNull DatabaseConfig database,
            Optional<SecurityConfig> security,
            Optional<Map<String, String>> features) {
        this.database = database;
        this.security = security.orElse(new SecurityConfig());
        this.features = features.orElse(Map.of());
    }
    
    @Validated
    public static class DatabaseConfig {
        @NotNull
        @Pattern(regexp = "^jdbc:.*", message = "Must be a valid JDBC URL")
        private final String url;
        private final String username;
        private final Duration timeout;
        private final int poolSize;
        
        @ConstructorBinding
        public DatabaseConfig(
                String url, 
                String username,
                Optional<Duration> timeout,
                Optional<Integer> poolSize) {
            this.url = url;
            this.username = username;
            this.timeout = timeout.orElse(Duration.ofSeconds(30));
            this.poolSize = poolSize.orElse(10);
        }
    }
}

Usage Example

 

app:
  database:
    url: jdbc:postgresql://localhost:5432/mydb
    username: ${DB_USER}
    timeout: PT30S
    pool-size: 20
  security:
    key-store: prod.jks
    session-timeout: PT1H
  features:
    new-auth-flow: true
…

java

@Service
@RequiredArgsConstructor
public class ExampleService {
    private final ApplicationConfig config;
    
    public void performOperation() {
        Duration timeout = config.getDatabase().getTimeout();
        // Use configuration...
    }
}

Explanation

Type-safe configuration properties solve several critical challenges in enterprise configuration management. When you implement them with Spring Boot's @ConfigurationProperties and Jakarta Bean Validation (JSR-380), you get comprehensive validation and type safety.

The design uses constructor binding (@ConstructorBinding) to ensure immutability, preventing runtime modifications that could lead to inconsistent application states. By organizing configurations with nested static classes, we can create logical hierarchies that make your structure more maintainable and self-documenting.

Key technical points:

  • @ConstructorBinding ensures configuration immutability by requiring all values to be set at construction time
  • Optional<T> parameters enable flexible configuration with sensible defaults, reducing boilerplate code
  • JSR-380 validation annotations (@NotNull@Pattern, etc.) provide immediate feedback on invalid configurations
  • Nested static classes create logical configuration groupings that mirror the YAML structure
  • Type-safe properties eliminate runtime type conversion errors common with @Value annotations
  • Configuration validation happens at application startup, failing fast if required properties are missing or invalid
  • The approach enables IDE auto-completion and type checking, improving developer productivity

 

Method 2: Profile-Based Configuration

Introduction

Different environments (development, staging, production) require different configurations while maintaining the same codebase.

Problem Tackled

  • Managing environment-specific settings
  • Switching configurations during development
  • Maintaining security across environments

Solution

Leverage Spring profiles with YAML configuration files.

Code Example

 

# application.yml
spring:
  profiles:
    group:
      development: dev-db, dev-cache
      production: prod-db, prod-cache

---
spring.config.activate.on-profile: dev-db
app:
  database:
    url: jdbc:h2:mem:devdb
    pool-size: 5

---
spring.config.activate.on-profile: prod-db
app:
  database:
    url: jdbc:postgresql://prod-host:5432/proddb
    pool-size: 50

Alternative Properties Approach
properties

# application-dev.properties
spring.datasource.url=jdbc:mysql://localhost:3306/devdb
spring.datasource.username=devuser

 

Usage Example

# Activate via command line
java -jar myapp.jar --spring.profiles.active=development

# Activate via environment variable
export SPRING_PROFILES_ACTIVE=development
java -jar myapp.jar

Explanation

Profile-based configuration solves several critical challenges in enterprise applications. The implementation leverages Spring Boot's profile activation mechanism to load environment-specific configurations automatically.  With YAML-based profiles, you can group related configurations (like dev-db with dev-cache) logically. If simplicity is what you prefer, properties files offer an alternative for basic scenarios.

Key technical points:

  • Profile groups reduce activation complexity by bundling related profiles
  • The spring.config.activate.on-profile selector ensures precise profile matching
  • Properties files (application-{profile}.properties) provide a straightforward way to organize configurations
  • Spring's profile resolution ensures the correct configuration is loaded based on the active profile
  • Property values can include references to environment variables for additional flexibility

Advanced Configuration Patterns

Secure Configuration Management

Enterprise applications often need to manage sensitive configuration data securely. As recommended in the Spring Cloud Vault Reference, sensitive configuration should be managed using secure vaults which provides robust integration with HashiCorp Vault for secure credential management.

 

@Configuration
@EnableVaultConfig
public class VaultConfig {
    @Value("${vault.uri}")
    private String vaultUri;
    
    @Bean
    public VaultTemplate vaultTemplate() {
        VaultEndpoint endpoint = VaultEndpoint.from(vaultUri);
        return new VaultTemplate(endpoint, 
            new TokenAuthentication(System.getenv("VAULT_TOKEN")));
    }
}

Dynamic Configuration Updates

For configurations that need to change without application restart, Spring Cloud Config with @RefreshScope enables dynamic updates:

@RefreshScope
@Service
@RequiredArgsConstructor
public class DynamicConfigService {
    private final VaultTemplate vaultTemplate;
    
    @Value("${app.feature.enabled:false}")
    private boolean featureEnabled;
    
    public void performOperation() {
        if (featureEnabled) {
            // Feature logic
        }
    }
}

Explanation

These advanced patterns address crucial enterprise requirements for security and flexibility. The Vault integration provides a secure way to manage sensitive configurations, while @RefreshScope enables runtime configuration updates without service interruption.

Key technical points:

  • Vault integration separates sensitive data from application code
  • @RefreshScope ensures beans can be recreated with updated configurations
  • Dynamic updates support feature flags and configuration changes in production
  • Secure credential rotation becomes possible without application restart
  • Centralized configuration management improves security and maintainability

 

Method 3: Command-Line and System Property Configuration

Introduction

Runtime configuration management often requires flexibility to override settings without rebuilding or redeploying applications. This is where command-line and system property configuration comes into play. Spring Boot’s support for command-line arguments provides a powerful way to dynamically override configuration properties during runtime, enabling quick adjustments across different environments. For more details, you can refer to the official Spring Boot documentation on command-line arguments.

Problem Tackled

  • Need for dynamic configuration changes in production
  • Environment-specific overrides without code changes
  • Quick testing and debugging scenarios
  • Emergency configuration updates
  • Different configurations for different deployment scenarios

Solution

Utilize command-line arguments and system properties to provide configuration overrides that take precedence over application properties and YAML files.

Code Example


@Configuration
@Slf4j
public class CommandLineConfig {
    @Bean
    public CommandLinePropertySource<?> commandLinePropertySource(
            ApplicationArguments args) {
        return new SimpleCommandLinePropertySource(args.getSourceArgs());
    }
    
    @PostConstruct
    public void logActiveProperties() {
        log.info("Active command line properties: {}",
                System.getProperties().stringPropertyNames().stream()
                    .filter(key -> key.startsWith("app."))
                    .collect(Collectors.toMap(
                        key -> key,
                        System::getProperty
                    )));
    }
}

Usage Example

 

# System Properties
java -Dapp.database.url=jdbc:postgresql://localhost:5432/testdb \
     -Dapp.database.pool-size=25 \
     -jar app.jar

# Command-line arguments
java -jar app.jar \
     --app.database.url=jdbc:postgresql://localhost:5432/testdb \
     --app.database.pool-size=25

# Environment variables
export APP_DATABASE_URL=jdbc:postgresql://localhost:5432/testdb
export APP_DATABASE_POOL_SIZE=25
java -jar app.jar

Explanation

Command-line and system property configuration provides a flexible way to override application settings at runtime, as specified in the Spring Boot External Configuration documentation. This approach is particularly valuable for DevOps scenarios where different environments require different configurations.

Key technical points:

  • Command-line arguments take highest precedence in Spring Boot's property hierarchy
  • System properties provide a robust way to set JVM-wide configuration
  • Properties can be changed without modifying application code or configuration files
  • Supports both relaxed binding (--app.database.url or APP_DATABASE_URL)
  • Enables quick configuration changes for testing and debugging
  • Provides audit trail through application logs

 

Method 4: External Configuration Sources

Introduction

Enterprise applications often require centralized configuration management and secure credential handling. Spring Cloud Config and Vault integration provide robust solutions for these requirements.

Problem Tackled

  • Centralized configuration management
  • Secure handling of sensitive data
  • Dynamic configuration updates
  • Configuration version control and audit
  • Environment-specific secret management

Solution

Integrate with external configuration services and secure vaults using Spring Cloud Config and HashiCorp Vault, following the Spring Cloud Config Server architecture.

Code Example

@Configuration
@EnableConfigurationProperties
@EnableConfigServer
public class ExternalConfigConfiguration {
    
    @Bean
    @ConfigurationProperties(prefix = "vault.config")
    public VaultProperties vaultProperties() {
        return new VaultProperties();
    }
    
    @Bean
    public VaultTemplate vaultTemplate(VaultProperties properties) {
        VaultEndpoint endpoint = VaultEndpoint.from(properties.getUrl());
        ClientAuthentication auth = new TokenAuthentication(
            properties.getToken());
        return new VaultTemplate(endpoint, auth);
    }
    
    @Bean
    @RefreshScope
    public ConfigurationPropertiesHolder configProps() {
        return new ConfigurationPropertiesHolder();
    }
}

@RefreshScope
@Component
@Slf4j
public class ConfigurationPropertiesHolder {
    @Value("${app.feature.enabled:false}")
    private boolean featureEnabled;
    
    @EventListener(RefreshScopeRefreshedEvent.class)
    public void onRefresh(RefreshScopeRefreshedEvent event) {
        log.info("Configuration refreshed, feature enabled: {}", 
                 featureEnabled);
    }
}

Usage Example

 

# bootstrap.yml
spring:
  cloud:
    config:
      uri: http://config-server:8888
      fail-fast: true
    vault:
      host: vault.example.com
      port: 8200
      scheme: https

# Trigger configuration refresh

curl -X POST http://localhost:8080/actuator/refresh

Explanation

External configuration sources provide a comprehensive solution for managing configurations across multiple services and environments, as detailed in the Spring Cloud documentation. This approach enables centralized configuration management while maintaining security and providing dynamic update capabilities.

Key technical points:

  • Spring Cloud Config Server provides version-controlled configuration management
  • Vault integration ensures secure credential handling and rotation
  • @RefreshScope enables dynamic configuration updates without application restarts
  • Event listeners allow for automated actions on configuration changes
  • Fail-fast capability ensures applications don't start with invalid configurations
  • Centralized management reduces configuration drift and improves security
  • Integration with Spring Boot Actuator enables configuration monitoring and refresh endpoints

 

Best Practices and Recommendations

Security

  • Never commit sensitive data to version control
  • Use secure vaults for credentials
  • Encrypt sensitive configuration values

Organization

  • Group related configurations
  • Use meaningful property names
  • Document configuration options

Validation

  • Implement validation for all critical configurations
  • Fail fast on invalid configurations
  • Provide clear error messages

Monitoring

  • Log configuration changes
  • Track configuration access patterns
  • Monitor for security violations

Explore More: How to Deserialize JSON in Java Using a Mapper

 

Conclusion

Managing environment configurations effectively is key to building secure, scalable, and maintainable applications. With Spring Boot’s powerful configuration features, you can set up type-safe, validated, and environment-specific settings that grow with your application. 

Whether you're working with microservices or a monolithic setup, this guide will help you apply best practices to keep your Java applications professional and resilient.

🚀 Looking for high-paying remote Java roles? Join Index.dev and get matched with top global companies. Work on innovative projects, grow your career, and enjoy the flexibility of remote work. Sign up today!

Share

Alexandr FrunzaAlexandr FrunzaBackend Developer

Related Articles

For Developers4 Easy Ways to Check for NaN Values in Python
Use np.isnan() in NumPy to check NaN in arrays. In Pandas, use isna() or isnull() for DataFrames. For single float values, use math.isnan(). If you're working with Decimal, use is_nan() from the decimal module. Each method fits different data types and use cases.
Ali MojaharAli MojaharSEO Specialist
For Employers12 Ways HR Teams Use Search and Content to Reach High-Intent Candidates
Tech HiringFreelance
Cold outreach is losing effectiveness. The smartest HR teams now attract high-intent candidates through SEO, AI-optimized content, and search intent strategies. Instead of chasing talent, they build content that gets discovered first.
Elena BejanElena BejanPeople Culture and Development Director