Create your own
Lesson illustration

Spring Profiles for Environment-Specific Configurations

Hello! Welcome to the next lesson in our course on preparing for microservices interviews.

In our last session, we made our microservice observable by implementing health check endpoints with Spring Boot Actuator. This is a critical step for ensuring a service is running and its dependencies are available. Now, we'll tackle another fundamental aspect of production-readiness: configuration.

A core principle of modern, cloud-native development (as outlined in the Twelve-Factor App methodology we touched on earlier) is to maintain a strict separation between code and configuration. You should be able to build your application artifact (e.g., a JAR file) once and deploy that exact same artifact to any environment—development, testing, staging, and production—without changing the code. This is only possible if environment-specific settings, like database URLs or API keys, are supplied externally.

This lesson focuses on how to achieve this elegantly using Spring Boot. For your interviews, being able to articulate a robust configuration management strategy is just as important as discussing architectural patterns.

By the end of this lesson, you will be able to configure application properties for different environments using Spring profiles.

1. The Challenge: Environment-Specific Configuration

Imagine you're developing an e-commerce microservice. Your local development setup might use an in-memory H2 database for speed and simplicity. The QA environment connects to a shared PostgreSQL database. The production environment connects to a highly-available, replicated database cluster with a different URL, username, and password.

How do you manage these different settings? A naive approach would be to have different versions of your application.properties file and manually swap them before building, but this is error-prone and violates the "build once, deploy anywhere" principle.

This is precisely the problem Spring Profiles are designed to solve.

Most wrongly answered Question Spring boot | Spring boot profiles Interview Questions | Code Decode

To start, let's watch a short clip from the video 'Most wrongly answered Question Spring boot' by Code Decode. It clearly explains why we need to segregate properties for different environments.

Watch the first 3 minutes and 26 seconds. Pay attention to the common examples given, such as different database connections and URLs for other services. This sets the stage for why a systematic solution is necessary.

2. Introducing Spring Profiles

A Spring Profile is essentially a label for a group of configuration settings. You can create different profiles for dev, qa, staging, and prod, and Spring Boot will apply the correct configuration based on which profile is active.

There are two primary ways to define profile-specific properties.

Method 1: Profile-Specific Property Files

The most common convention is to create separate property files for each environment, following the naming pattern application-{profileName}.properties.

  • application.properties: Contains common properties that apply to all environments.
  • application-dev.properties: Contains properties specific to the dev environment.
  • application-prod.properties: Contains properties specific to the prod environment.

When a profile (e.g., dev) is active, Spring Boot loads both application.properties and application-dev.properties. If a property exists in both files, the value from the more specific profile file (application-dev.properties) overrides the default one.

Most wrongly answered Question Spring boot | Spring boot profiles Interview Questions | Code Decode

Let's continue with the 'Code Decode' video to see a practical demonstration of creating and using these files.

Watch from 04:54 to 10:29. This segment walks you through creating profile-specific files and shows how Spring automatically picks up the correct properties when a profile is activated.

Method 2: Multi-Document YAML Files

If you prefer using YAML (.yml), you can define all your profiles within a single application.yml file. This can be convenient for seeing all configurations in one place. You separate the profile sections using three hyphens (---).

The first section (before any ---) contains the default properties. Subsequent sections are activated using the spring.config.activate.on-profile property.

Mastering Spring Boot Configuration

The article 'Mastering Spring Boot Configuration' provides a clear example of this modern approach.

Read the section titled 'Profile-Based Configuration'. Notice how the default, staging, and production datasource URLs are all defined in one file, which makes comparisons easy. The key properties here are --- and spring.config.activate.on-profile.

Here's what that looks like:

# Default configuration (applies to all profiles)
server:
  port: 8080
spring:
  application:
    name: order-service
  datasource:
    url: jdbc:h2:mem:testdb # Default for local dev

---

# Staging environment
spring:
  config:
    activate:
      on-profile: staging
  datasource:
    url: jdbc:postgresql://staging-db:5432/orders

---

# Production environment
spring:
  config:
    activate:
      on-profile: prod
  datasource:
    url: jdbc:postgresql://prod-db:5432/orders
    # Use environment variables for secrets!
    username: ${DB_USER}
    password: ${DB_PASSWORD}

3. Activating Profiles: The Right Way and the Wrong Way

This is a critical topic for interviews. How you activate a profile determines how portable your application is.

The Anti-Pattern: Hardcoding in application.properties

You can activate a profile by setting spring.profiles.active=dev in your main application.properties file.

This is a bad practice and an anti-pattern. Why? Because it embeds the environment configuration into the application artifact itself. If you build a JAR file with this property set to dev, it will always run with the dev profile, even when you deploy it to the production server. This defeats the entire purpose of having a single, environment-agnostic build.

Most wrongly answered Question Spring boot | Spring boot profiles Interview Questions | Code Decode

The 'Code Decode' video dedicates a section to explaining exactly why this is a bad idea. Understanding this is key to demonstrating production-level thinking.

Watch the segment from 29:00 to 32:00. The speaker clearly articulates why you should never commit spring.profiles.active into your application.properties file.

The Recommended Approach: External Configuration

The correct way to activate a profile is to provide it externally when you run the application. This respects the separation of config from code and allows your CI/CD pipeline to deploy the same artifact to different environments by simply changing a startup parameter.

Here are the most common methods, in order of precedence (highest first):

  1. Command-line argument:
    java -jar my-app.jar --spring.profiles.active=prod
  2. Environment variable:
    export SPRING_PROFILES_ACTIVE=prod
    java -jar my-app.jar
  3. JVM system property:
    java -Dspring.profiles.active=prod -jar my-app.jar

Spring profiles explained - Microservice configuration with Spring Boot [08]

The video 'Spring profiles explained' by Java Brains provides an excellent explanation of why activating profiles via command-line arguments is the key to achieving a truly portable application.

Watch from 11:31 to 13:13. This clip connects the concept of profiles directly to the goal of having one JAR that can be deployed anywhere, which is a powerful point to make in an interview.

Since you are interested in Kubernetes and modern deployment practices, it's useful to see how this plays out in containerized environments.

Mastering Spring Boot Configuration

Let's revisit the 'Mastering Spring Boot Configuration' article to see how this is applied in real-world deployment scenarios.

Review the 'Deployment Strategies' section. Focus on the examples for Docker, Docker Compose, and Kubernetes. Notice how in each case, the SPRING_PROFILES_ACTIVE environment variable is used to activate the desired profile on-the-fly.

Test your understanding!

You have an application.properties file with logging.level.root=INFO. You also have an application-dev.properties file with logging.level.root=DEBUG.

If you run your application with the command java -jar app.jar --spring.profiles.active=dev, what will be the effective root logging level and why?

Show answer

The effective root logging level will be DEBUG.

When the dev profile is activated, Spring Boot loads both application.properties and application-dev.properties. Since the logging.level.root property exists in both, the value from the more specific profile file (application-dev.properties) overrides the default one.

4. Profile-Specific Beans with @Profile

Beyond just properties, you can also use profiles to conditionally register Spring beans. This is extremely powerful for scenarios where you need different component implementations for different environments.

A classic example is creating a mock service for local testing. You could have a RealPaymentGateway bean and a MockPaymentGateway bean. By using the @Profile annotation, you can tell Spring which one to instantiate.

Profiles In Spring & Spring Boot Overview
This image provides a great visual summary of what Spring Profiles are and how the `@Profile` annotation can be used to conditionally enable beans for specific environments like 'prod' or 'dev'.
// This bean will only be created when the 'prod' profile is active
@Component
@Profile("prod")
public class RealPaymentGateway implements PaymentGateway {
    // ... logic to connect to a real payment provider
}

// This bean will only be created when the 'dev' or 'test' profile is active
@Component
@Profile({"dev", "test"})
public class MockPaymentGateway implements PaymentGateway {
    // ... mock logic that returns dummy responses
}

This prevents you from needing if/else blocks in your code to switch between implementations. The Spring container handles it for you at startup.

Spring profiles explained - Microservice configuration with Spring Boot [08]

The Java Brains video also has a great segment explaining this powerful feature.

Watch from 13:13 to 15:44. The example of having different data source beans for different profiles is a perfect illustration of this concept's power.

A word of caution: While powerful, use @Profile on beans responsibly. Overusing it can make your application's configuration complex and harder to reason about. If not managed carefully, it can lead to NoSuchBeanDefinitionException or other bean creation errors if a required bean is missing from a specific profile.

Conclusion

You now have a solid understanding of how to manage environment-specific configurations in a clean, scalable, and production-ready way. This is a non-negotiable skill for any developer working with microservices.

Key Takeaways:

  • Purpose: Spring Profiles allow you to separate configuration from code, enabling a "build once, deploy anywhere" strategy.
  • Definition: Define profile-specific properties using application-{profile}.properties files or within a single application.yml using the --- separator.
  • Activation (The Right Way): Always activate profiles using external mechanisms like environment variables (SPRING_PROFILES_ACTIVE) or command-line arguments (--spring.profiles.active).
  • Activation (The Wrong Way): Avoid hardcoding spring.profiles.active in your application.properties file, as it creates a tightly coupled, non-portable artifact.
  • Conditional Beans: Use the @Profile annotation to register different bean implementations for different environments (e.g., real vs. mock services).

During an interview, if you are asked about deploying a service to production, you should confidently describe this workflow: you build a single JAR, package it into a Docker image, and use environment variables in your Kubernetes deployment manifest to set SPRING_PROFILES_ACTIVE=prod. This demonstrates a clear understanding of modern DevOps and cloud-native principles.

Next Up

Our services are now observable and configurable. But what happens when things go wrong during a request? Simply returning a stack trace is unprofessional and exposes internal implementation details. In our next lesson, we will learn how to implement standardized error responses across a service using global exception handlers (@ControllerAdvice).

Can't find a good explanation? Sign up and we'll make it for you

Sign up