CodeGym /Courses /Spring Boot /How to enable a profile and where it comes from

How to enable a profile and where it comes from

Spring Boot
Level 15, Lesson 1
Available

1. A profile is a property, not a button

At the beginning, the most common problem is a practical one: you launch catalog-service and are sure it starts “as usual”, but then it suddenly behaves differently. A different port, some settings are off, the landing page header is unexpected. In 90% of cases, this is not “Boot broke”, but the wrong profile became active.

The most important idea in this lecture sounds boring, but it saves hours of your life: an active profile is not a separate entity “outside configuration”; it is an ordinary property that ends up in Environment and participates in the overall precedence system. That is, spring.profiles.active can come from YAML, an environment variable, or a command-line argument just as well — and it can also be overridden by you accidentally.

This leads to a useful habit: when you discuss “which profile is enabled”, you are really discussing “where the value of the spring.profiles.active property came from”. And if you are used to thinking about properties as “layers”, then a profile is just a layer that determines which other configuration layers will be included next (for example, which application-{profile}.yaml files will be applied — that is the topic of the next lecture).

Another important point: a profile is not a way to “make a separate application”. It is a mode of one and the same application. So in the training project, we will try to keep profile selection predictable: one place, one mechanism, minimum surprises. Spring allows a million variants, but we are not going to turn catalog-service into a configuration quest with secret doors.

2. spring.profiles.active: what kind of key is this

When you hear “active profile”, it is easy to imagine an abstract picture: “Spring somewhere deep in the framework enables dev mode.” In practice, it is more concrete. The profile is activated through the spring.profiles.active key — and it is important to know its exact name, because typos in config do not complain with a teacher’s voice; they complain with quiet incorrect behaviour.

In YAML, it looks like this (nested notation is just another form of the spring.profiles.active key):

spring:
  profiles:
    # Activates the application profile (the same as spring.profiles.active)
    active: "dev"

And yes, the value can be more than one word. Spring Boot accepts multiple profiles separated by commas, for example "local,dev". But how exactly the final configuration is assembled when multiple profiles are involved, and why the last wins rule works, we will cover in the next lecture so as not to mix topics.

Why did I say that spring.profiles.active is read “very early”? Because Boot must understand the profiles before it starts selecting profile-specific files and activating profile-dependent YAML documents. Otherwise, you would get a logical loop: “to read the profile, you need to know the profile.” So profile selection is one of the early steps in configuration startup.

There is another nuance that is especially useful for beginners. If there are no active profiles, Environment.getActiveProfiles() will return an empty array. But Spring also has the concept of a default profile (often the string "default"). This is not “your local”, it is just an internal base mode. In our course, we will call the situation “no active profiles” base so as not to confuse people: base means “we work with application.yaml without profile-specific layers”.

3. Method #1: set the profile in application.yaml

The most straightforward way to enable a profile is to define it directly in the base application.yaml. It looks like “I put the setting in the file — it will always be like that.” For learning, this is nice: fewer moving parts, fewer questions like “where did I set this?”. But in real projects, this approach can become a “slow-burning mine”.

The simplest variant:

spring:
  application:
    name: "catalog-service"
  profiles:
    # IMPORTANT: this hard-locks the dev profile until someone overrides it higher in precedence
    active: "dev"

Such a variant is useful as a mechanical example: it makes the exact key name easy to see. But for a live project, this is too rigid a baseline.

If you do this and commit it to the repository, the application will always start in dev until someone outside overrides this value with a higher-priority source. And that is where it gets “dangerous”: once you get used to dev being enabled “by default”, one day you will launch the service where you expected prod and be surprised (and then surprise your colleagues).

That is why in the training project we usually choose a slightly more mature, but still understandable scheme: take the active profile from an external variable, and keep a fallback in YAML. This nicely demonstrates externalized configuration and does not force you to remember each time exactly how you started the application.

For example:

spring:
  application:
    name: "catalog-service"
  profiles:
    # Take the value from the APP_PROFILE environment variable; if it is absent, use local
    active: "${APP_PROFILE:local}"

What happens here? If the environment contains the APP_PROFILE variable, Boot will substitute its value. If the variable is missing, the fallback local will be used. The result is predictable: on your machine, the default is local, but you can easily switch it with an external setting without editing the file.

For catalog-service, this is the calm default scheme: the base file provides a fallback to local, and SPRING_PROFILES_ACTIVE, -Dspring.profiles.active=..., and --spring.profiles.active=... simply override the same spring.profiles.active for a specific run.

And one more rule worth stating in advance: if a project chooses a file-based profile by default, let it be defined only in the base file. Trying to hide profile selection in application-dev.yaml is like trying to write on a notice, “to read this notice, first read this notice.” Boot will not read application-dev.yaml at all until the dev profile is activated.

4. Method #2: profile via env vars

Environment variables are the “control panel for the application” in any modern backend life. They are convenient because they do not require recompilation, they do not require editing the jar, and they fit any environment well: terminal, IDE, CI, server. They also clearly demonstrate that spring.profiles.active is just another property: it can come from outside and “beat” what is written in YAML.

Spring Boot has an official env var for enabling a profile: SPRING_PROFILES_ACTIVE. It is automatically mapped to spring.profiles.active thanks to relaxed binding (we have already seen this idea with other properties). Example for a Unix-like shell:

# Set the official Spring Boot env var
export SPRING_PROFILES_ACTIVE=dev
# Run the application (the profile will be taken from the environment variable)
./gradlew bootRun

For PowerShell, it looks similar:

# Set the environment variable for the current PowerShell session
$env:SPRING_PROFILES_ACTIVE="dev"
# Run the application
.\gradlew.bat bootRun

If you are using the variant from the previous section with the ${APP_PROFILE:local} placeholder, then you can also set APP_PROFILE:

# This is not a “Spring” variable; it is your own (the placeholder in YAML reads it)
export APP_PROFILE=dev
./gradlew bootRun

It is important not to get confused here: APP_PROFILE is not a “magic Spring variable”. It is simply your own variable that you bound in YAML. And SPRING_PROFILES_ACTIVE is the native Spring Boot variable that works without any placeholders.

The practical takeaway is simple: choose one style and stick to it. If the team agrees that “we set the profile through SPRING_PROFILES_ACTIVE”, do not add APP_PROFILE in parallel, otherwise in a month you will be trying to figure out “why Petya has dev and Masha has local”, and that will be about config, not people.

To make the picture clearer, here is a small table (for learning purposes, not an exhaustive list):

Channel What it looks like Example value
Env var (official)
SPRING_PROFILES_ACTIVE
local / dev / prod / local,dev
Env var (your own, via placeholder)
APP_PROFILE
local / dev / prod

And, as we already know from the precedence topic, env vars usually beat values from packaged application.yaml. So “I put dev in YAML, but it is still local” very often means: somewhere SPRING_PROFILES_ACTIVE=local is set (in the terminal, in the IDE, in the system), and it quietly wins.

5. Method #3: profile via -D... and the IDE

If env vars are the “control panel at the operating system level”, then system properties (-D...) are the “control panel at the JVM level”. They also participate in property sources and usually have high priority. They are easy to experiment with locally, especially when you launch the application from an IDE and want to switch the profile without editing configs and without exporting variables in the terminal.

The most direct example looks like this:

# Pass the profile as a JVM system property
java -Dspring.profiles.active=dev -jar catalog-service.jar

Yes, we have not yet reached packaging bootJar in the course, but it is important to understand the principle: -D... goes into Java System Properties, and Boot reads it as one of the configuration sources.

In an IDE, this is usually even simpler. In IntelliJ IDEA, for example, you can add VM options to the run configuration:

# VM options: the profile will be read from System Properties
-Dspring.profiles.active=dev

This gives you a convenient local toggle: one run — local, another — dev. But there is a “minus-trap” here: IDEs like to remember settings. You can set -Dspring.profiles.active=prod once “just to check”, and then forget about it a week later and keep launching the same way. Then you will look at the code and YAML like honest people, while the culprit will be one little flag in the run configuration.

From the perspective of precedence, it is useful to keep a simplified ladder in mind: command-line arguments are usually stronger than system properties, system properties are often stronger than env vars, and env vars are often stronger than YAML. The full ladder is broader, but for a profile this model is enough.

Another methodological thought: system properties are a good tool for local development and experiments, but as the “main way to choose an environment” they usually lose to env vars. Simply because env vars are easier to standardize and easier to pass into different environments without binding to a specific java command.

6. Method #4: command-line arguments (--...)

Command-line arguments in the --key=value style are Spring Boot’s favourite way of saying: “if you want to override something quickly and precisely, do it here.” They are almost always on the top levels of precedence, so they are an excellent tool for a “one-off run”: test a hypothesis, compare two behaviours, quickly enable a profile for a specific start.

For a profile, it looks like this:

# Pass Spring Boot arguments through Gradle bootRun
./gradlew bootRun --args="--spring.profiles.active=dev"

In an IDE, the same thing is usually done through Program arguments:

# Program arguments: these are not VM options, but application arguments
--spring.profiles.active=dev

Compare the feeling. An env var has to be exported somewhere and then you have to remember to clear it. A system property has to be removed from VM options. A CLI argument lives next to a specific run and does not pretend to be a “forever” setting. That is why this method is especially pleasant for learning experiments.

Another useful detail: if you need to specify multiple profiles, you do it as a single value separated by commas:

# Multiple profiles are listed with commas
./gradlew bootRun --args="--spring.profiles.active=local,dev"

But how exactly they will overlap, which files will be picked up, and why order matters — that is literally the topic of the next lecture (about application-{profile}.yaml and last wins). Right now, the only important thing to remember is that CLI args are a very strong source, and that is why they sometimes “break expectations”: you think the profile is taken from YAML, but it is taken from --spring.profiles.active=..., which you added to the run a week ago.

To reinforce this, you can imagine a small layered cake (simplified):

flowchart TB
    A["--spring.profiles.active=... (CLI args)"] --> R[Final active profile]
    B["-Dspring.profiles.active=... (System properties)"] --> R
    C["SPRING_PROFILES_ACTIVE=... (Env vars)"] --> R
    D["application.yaml (packaged config)"] --> R
    E["nothing set (base / default)"] --> R

The diagram is not about all sources “adding up”; it is about them competing. The one with the higher priority in the overall property sources model wins. And, as we already know, that might not be YAML at all.

That is why this is not four competing architectures, but four channels for choosing the same property. A project usually needs one clear default in application.yaml, and the other methods remain external overrides for a specific run.

7. How to check the active profile

We have already done the basic check for active profiles through Environment. Here it is useful to extend the same StartupSummaryRunner one step further: in addition to the list of active profiles, you often need to see the raw value of spring.profiles.active that came in from property sources.

import java.util.Arrays;

// Inside the already familiar StartupSummaryRunner, where env is already injected as Environment
String[] active = env.getActiveProfiles();

// Was the property explicitly set by any configuration source
String configured = env.getProperty("spring.profiles.active", "<not set>");

System.out.println("activeProfiles=" + Arrays.toString(active)); // activeProfiles=[dev]
System.out.println("spring.profiles.active=" + configured);      // spring.profiles.active=dev

Why is it useful to look at both values? getActiveProfiles() shows what actually became active after all overrides, while getProperty(...) helps you understand whether spring.profiles.active was explicitly set and what exactly came from the configuration sources. For example, if the profile is not set, spring.profiles.active may end up as <not set>, while active profiles will be [].

Then comes the nicest part: you stop arguing with YAML, the IDE, and your own memory of yesterday’s run. Launched with an env var — you saw it. Launched with --spring.profiles.active=... — you saw it. And if the profile came from somewhere other than you expected, Environment will honestly show that.

This is a small piece of discipline that later helps a lot in more advanced topics: configuration introspection through Actuator, profile tests, precedence diagnostics. But for now, one habit is enough for us: always check what actually happened, not what “probably should have happened”.

8. Typical mistakes when enabling a profile

Profile mistakes often look like “Spring Boot quirks”, but almost always they are simple human confusion about configuration sources. The good news: they are fixed not by “deep knowledge”, but by careful discipline — one way to choose a profile and a habit of checking the result through Environment.

Mistake #1: the profile is set in three places at once, and you believe the most convenient one.
The most common scenario: application.yaml contains ${APP_PROFILE:local}, the IDE has -Dspring.profiles.active=dev set, and the terminal still has SPRING_PROFILES_ACTIVE=prod left over from yesterday’s experiment. As a result, the application starts in prod, you swear that you “definitely ran dev”, and you start digging into the code. The correct treatment is to choose one main channel for profile selection and remove the extras, and in diagnostics always print the active profiles at startup.

Mistake #2: expecting “application.yaml always wins” because it looks nice and lives in the project.
After Java Core, the brain wants to believe in “one source of truth”. But in Boot, truth is layered, and application.yaml is just one of the sources. If a profile is set through env vars or CLI args, YAML did not “lose” — it was simply lower in priority. The habit of “checking precedence” saves more time than any debugging session in the IDE.

Mistake #3: trying to activate a profile from application-dev.yaml.
This is the classic logical loop. application-dev.yaml is read only if the dev profile is already active, so the phrase “inside application-dev.yaml I will set spring.profiles.active=dev” does not work as expected. The active profile is chosen earlier. If you want a default profile, set it in the base application.yaml or from the outside at launch time.

Mistake #4: the wrong property name or “almost the same thing”.
spring.profile.active, spring.profiles=dev, spring.profiles.activeProfile — all of this looks plausible, but Boot does not understand it. Unlike Java code, YAML will not stop you at compile time. So it is better to remember the key name exactly once: spring.profiles.active.

Mistake #5: the profile value is set with the “wrong” separator.
Spring expects a list of profiles separated by commas. If you write local dev with a space or local;dev with a semicolon, you will get one strange profile named local dev or local;dev. And then it gets even more fun: Boot will start looking for application-local dev.yaml (yes, with a space), and you will think it “cannot see the file”.

1
Task
Spring Boot, level 15, lesson 1
Locked
Default profile from `application.yaml`
Default profile from `application.yaml`
1
Task
Spring Boot, level 15, lesson 1
Locked
Profile Override by a Command-Line Argument
Profile Override by a Command-Line Argument
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION