Multi-Environment Configuration in Spring Boot

A Spring Boot application is like a chameleon: it needs to adapt to different environments without changing its core identity. Whether it’s running locally on my machine, in a development environment, or in the production environment, the code remains the same.

But the configuration? That’s a different story.

From database URLs to API keys, every environment has its quirks. And if I’m not careful, I’ll end up with a configuration mess that’s harder to untangle than a plate of spaghetti.

In this article, I’ll walk through how to set up a multi-environment configuration in Spring Boot, because nobody wants to be the developer who hardcodes production credentials into their local setup. (We’ve all been there, but let’s not talk about it.)

Maven

The journey to a clean multi-environment setup starts with Maven (or Gradle, if you’re feeling fancy). Maven profiles are my first line of defense against configuration chaos.

By defining profiles for each environment, I ensure that the correct Spring Boot profile is activated when I build my application.

Here’s how to set it up in your pom.xml:

<profiles>
    <profile>
        <id>local</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <spring.profiles.active>local</spring.profiles.active>
        </properties>
    </profile>
    <profile>
        <id>dev</id>
        <properties>
            <spring.profiles.active>dev</spring.profiles.active>
        </properties>
    </profile>
    <profile>
        <id>staging</id>
        <properties>
            <spring.profiles.active>staging</spring.profiles.active>
        </properties>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <spring.profiles.active>prod</spring.profiles.active>
        </properties>
    </profile>
</profiles>

With this setup, I can build my application for a specific environment using the -P flag:

mvn clean install -Pprod

This ensures that the correct Spring Boot profile (prod) is activated, loading the appropriate configuration files. Because nothing says “professional” like a well-organized pom.xml.

Environment Variables

While Maven profiles handle the build-time configuration, environment variables are my go-to solution for runtime flexibility. Spring Boot loves environment variables. By using environment variables, I can override configuration properties without touching my code.

For example, I can define a DATABASE_URL in my application.yml:

spring:
  datasource:
    url: ${DATABASE_URL:jdbc:h2:mem:localdb}

Here, DATABASE_URL is an environment variable. If it’s not set, Spring Boot will default to the in-memory H2 database. This is great for local development, but in other environments, I can set the variable to point to the correct database.

Pro Tip: Even in local development, set up environment variables. It’s a small effort that pays off when I’m debugging why my app works locally but not in staging. (Spoiler: It’s always a missing environment variable.)

DevOps

Now that my application is configured to handle multiple environments, it’s time to automate the deployment process. This is where CI/CD pipelines come in. Whether I’m using Jenkins, GitHub Actions, or GitLab CI, the goal is the same: use the correct configuration for the target environment.

Here’s how to do it:

  1. Branch-Based Configuration: Map Git branches to environments. For example:
    • main → production
    • release → staging
    • develop → development
  2. Environment-Specific Commands: In my CI/CD pipeline, I use the appropriate Maven profile based on the branch:
jobs: 
  build: 
    runs-on: ubuntu-latest 
      steps: 
      - name: Checkout code 
        uses: actions/checkout@v2
      - name: Build with Maven 
        run: mvn clean install -P${{ github.ref == 'refs/heads/main' && 'prod' || 'dev' }}
  1. Secrets Management: I use the CI/CD tool’s secrets management to inject environment-specific variables (like database credentials or API keys) during the build process.

Conclusion

My Spring Boot application might be a masterpiece of clean code and elegant design, but if the configuration is a mess, it’s all for nothing. By using Maven profiles, environment variables, and CI/CD pipelines, I can ensure that the app runs smoothly in every environment, from my local machine to the production server.

Remember, the goal is to make the differences between environments transparent. You shouldn’t have to think about whether you’re running in local or production, your app should just work. And if it doesn’t, well, at least you’ll know it’s not because of a missing environment variable. (Probably.)


Never Miss Another Tech Innovation

Concrete insights and actionable resources delivered straight to your inbox to boost your developer career.

My New ebook, Best Practices To Create A Backend With Spring Boot 3, is available now.

Best practices to create a backend with Spring Boot 3

Leave a comment

Discover more from The Dev World - Sergio Lema

Subscribe now to keep reading and get access to the full archive.

Continue reading