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:
- Branch-Based Configuration: Map Git branches to environments. For example:
main→ productionrelease→ stagingdevelop→ development
- 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' }}
- 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.)



Leave a comment