Maven ·
‹ Previous Next ›
⏱ 5 min read Modified: 2026-11-07

Maven Profiles: Declaration, Activation, -P and activeByDefault

A pom.xml has two profiles: dev with <activeByDefault>true</activeByDefault>, which sets the database address db.url, and it for integration tests. A developer runs mvn verify -P it, the build passes, but target/classes/app.properties contains the line db.url=${db.url} instead of the database address. The dev profile switched off silently: activeByDefault works only while no other profile in that POM is active.

A Maven profile is a named set of POM settings inside the <profiles> element that is applied on top of the main model only when activated: manually with the -P option or automatically by a condition from <activation> (a property, the JDK version, the operating system, the presence of a file). Profiles let one build behave differently in different environments without copies of pom.xml. The examples target Maven 3.9.x.

Why Maven Profiles Are Needed

Maven profiles are needed when the same project has to be built slightly differently: with other properties for an environment, with extra checks on CI, with slow tests or a module only on request, with settings for a specific JDK version or OS. Instead of several copies of pom.xml, the differences are described as profiles, and the needed set is switched on at run time.

Scenario What the profile changes How it is usually switched on
dev / test / prod environments Properties (service address, log level) substituted into resources -P prod; dev — by default
CI checks Static analysis, coverage, artifact signing plugins By property (-Dci) or -P ci in the CI script
Integration tests The failsafe plugin, a test database, extra dependencies -P it
Optional module <modules>: the module joins the build only when activated -P e2e
Different JDKs and OSes Compiler parameters, native libraries, paths Automatically: <jdk>, <os>

A profile changes the output of the build: a jar built with -P prod differs from one built without it. So settings that can be supplied when the application starts (environment variables, external config) are better not baked into the build — more in the sections Maven Profiles vs Spring Profiles and Why Not to Keep Passwords in Profiles.

How to Declare a Profile: profiles, profile, id

Profiles are declared in the <profiles> element of pom.xml: each <profile> must have a unique <id>, which is used to switch it on with -P, may contain an <activation> condition, and may contain any sections a profile is allowed to override (<properties>, <dependencies>, <build> and others).

<profiles>
    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <app.env>dev</app.env>
            <db.url>jdbc:h2:mem:shop</db.url>
        </properties>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <app.env>prod</app.env>
            <db.url>jdbc:postgresql://db.example.com/shop</db.url>
        </properties>
    </profile>
</profiles>

Profile properties are substituted in the same places as regular properties from <properties>: dependencies, plugin configuration, filtered resources. Manual activation with mvn -P:

mvn package -P prod          # activate prod
mvn package -P prod,it       # several profiles
mvn package -P '!dev'        # deactivate dev (quote the ! in bash)

In a multi-module project a profile from -P is switched on in every reactor module where a profile with that id is declared (including through the parent POM). The external profiles.xml file next to pom.xml, which appears in old articles, existed in Maven 2; Maven 3 does not support it.

What a Profile Can Override

A profile in pom.xml can contain almost the same sections as the POM itself: properties, dependencies, modules, plugins, resources, repositories. Off limits are the project coordinates, <parent> and part of the <build> settings — the source and output directories, as well as build extensions.

Section Allowed in a POM profile? Typical use
properties Yes Database URL, environment name, plugin flags
dependencies, dependencyManagement Yes A database driver or test library for one environment only
modules Yes An extra module that is built only on request
build: plugins, pluginManagement, resources, testResources, filters, finalName, defaultGoal Yes Enable code checks on CI, a different resources directory or filter file
repositories, pluginRepositories, distributionManagement, reporting Yes A different repository for publishing
build: sourceDirectory, testSourceDirectory, outputDirectory, testOutputDirectory, extensions No —
groupId, artifactId, version, packaging, parent No —

The "extra module" scenario. Slow end-to-end tests are moved into a shop-e2e-tests module that joins the reactor only in a profile:

<!-- shop-parent/pom.xml -->
<profiles>
    <profile>
        <id>e2e</id>
        <modules>
            <module>shop-e2e-tests</module>
        </modules>
    </profile>
</profiles>

A plain mvn verify builds the three shop modules, and mvn verify -P e2e builds four. How aggregation and the reactor work is covered in the lesson Maven Multi-Module Project.

The "plugin only in a profile" scenario. A plugin declared in the <build><plugins> of a profile is added to the plugins of the main model; an execution with the same id is merged with the main one. That is how maven-failsafe-plugin goes into the it profile (why it is needed — in the section Unit vs Integration Tests: Surefire and Failsafe, how to write executions — in the section Binding Goals to Phases: executions).

Maven Profile Activation: -P, activeByDefault, activation

A Maven profile is activated in three ways: explicitly with -P (or through <activeProfiles> in settings.xml), by default through <activeByDefault>, or automatically when an <activation> condition is met: a property is set, the JDK version or OS matches, or a file exists. Explicit deactivation with -P '!id' switches a profile off even if its condition is met.

Method Example When the profile is active
Manually mvn verify -P prod,it The profile is named in -P (comma-separated) or in <activeProfiles> of settings.xml
Deactivation -P '!dev' or -P -dev Never in this run, even if its condition is met
By default <activeByDefault>true</activeByDefault> If no other profile in the same POM is active (see the note below)
Property is set <property><name>ci</name></property> A system or user property is set with any value: -Dci
Property is not set <property><name>!ci</name></property> There is no ci property
Property value <property><name>env</name><value>ci</value></property> -Denv=ci; the value !ci, according to the Maven documentation, means the property is not set or its value is not ci
JDK version <jdk>25</jdk>, <jdk>[17,21)</jdk>, <jdk>!1.8</jdk> Maven runs on a JDK whose version starts with the prefix, falls within the range, or (with !) does not start with the prefix
OS <os><family>windows</family></os> OS family (windows, unix, mac); name, arch, version are also available
File <file><exists>${basedir}/local.properties</exists></file> The file exists (exists) or is missing (missing)

An example of automatic activation: the ci profile is switched on when the -Dci property is set, and the legacy-jdk profile on a JDK from 17 inclusive up to 21 exclusive:

<profile>
    <id>ci</id>
    <activation>
        <property>
            <name>ci</name>
        </property>
    </activation>
    <!-- static analysis plugins, coverage etc. -->
</profile>
<profile>
    <id>legacy-jdk</id>
    <activation>
        <jdk>[17,21)</jdk>
    </activation>
</profile>

If one <activation> contains several conditions, in Maven 3.2.2 and later the profile is activated only when all of them are met (in older versions one was enough). In Maven 3, environment variables are available to the <property> condition as properties with the env. prefix (for example, env.CI). The <file> path may use ${basedir}, system properties and environment variables, but not values from <properties>. Check the result with help:active-profiles rather than from memory.

Watch out: activeByDefault switches off silently

A profile with activeByDefault stays active only while no other profile in the same POM is activated — with -P or by a condition. The command mvn verify -P it from the start of the lesson switches on it and thereby switches off dev: the db.url property disappears, and the filtered resource keeps ${db.url}. The same happens if someone adds a profile that activates automatically by JDK. A more reliable default is activation by the absence of a property: <property><name>!prod</name></property> (active until -Dprod is set), or listing the needed profiles explicitly: -P dev,it.

Fine print: property activation does not see <properties>

In Maven 3 the <property> condition checks system properties and command-line properties (-D), but not the values from the <properties> section of the POM. Writing <env>ci</env> in <properties> will not activate a profile with the env=ci condition — neither in the main model nor inside another profile. Pass such values with -Denv=ci or through the .mvn/maven.config file in the project root.

Profiles and Resources for Different Environments

To give the application its environment settings, profile properties are substituted into filtered resources: the profile sets db.url, and when Maven copies src/main/resources, it replaces ${db.url} expressions in the files with the value from the active profile. How to enable filtering (<filtering>true</filtering>) and which files to filter is covered in the section The build Section: finalName, Directories, Resources, Plugins.

With the dev and prod profiles from the section How to Declare a Profile and filtering enabled, the template and the result look like this:

# src/main/resources/app.properties
app.env=${app.env}
db.url=${db.url}

# target/classes/app.properties after: mvn package
app.env=dev
db.url=jdbc:h2:mem:shop

# target/classes/app.properties after: mvn package -P prod
app.env=prod
db.url=jdbc:postgresql://db.example.com/shop

If there are many environment properties, they are kept not in the POM but in separate filter files: the profile sets <build><filters><filter>src/main/filters/prod.properties</filter></filters></build>, and the values from that file are also substituted into filtered resources. Remember that the result is different artifacts: a jar for prod cannot be checked on a test environment without rebuilding it.

Profiles in settings.xml and activeProfiles

Profiles can also be declared in ~/.m2/settings.xml, but they are limited there: only <activation>, <properties>, <repositories> and <pluginRepositories> are allowed. They usually hold machine-specific settings — a corporate repository address or local paths — and the profile is kept permanently on through <activeProfiles>.

<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0">
    <profiles>
        <profile>
            <id>company</id>
            <repositories>
                <repository>
                    <id>company-snapshots</id>
                    <url>https://nexus.example.com/repository/maven-snapshots/</url>
                </repository>
            </repositories>
        </profile>
    </profiles>
    <activeProfiles>
        <activeProfile>company</activeProfile>
    </activeProfiles>
</settings>

An id in <activeProfiles> activates the profile with that name on every Maven run on this machine, as if it were passed in -P. Keep everything that affects the build result in pom.xml, otherwise the project builds differently for your colleagues and on CI. The rest of settings.xml (mirrors, credentials, the repository path) is covered in the section settings.xml: Mirrors, Credentials and the Repository Path.

Which Profiles Are Active: help:active-profiles

To find out which profiles Maven actually applied, run mvn help:active-profiles: for each module it prints the active profiles and the source of each one (a POM or settings.xml). The full list of declared profiles with an "active" flag is printed by mvn help:all-profiles, and the final model with the profiles applied by mvn help:effective-pom with the same -P options.

Approximate output for a module whose parent declares dev with activeByDefault:

mvn help:active-profiles -pl shop-web

Active Profiles for Project 'com.examclouds:shop-web:jar:1.0-SNAPSHOT':

The following profiles are active:

 - dev (source: com.examclouds:shop-parent:1.0-SNAPSHOT)

Approximate output of help:all-profiles (the format depends on the maven-help-plugin version):

mvn help:all-profiles -N

[INFO] Listing Profiles for Project: com.examclouds:shop-parent:pom:1.0-SNAPSHOT
  Profile Id: dev (Active: true, Source: pom)
  Profile Id: prod (Active: false, Source: pom)
  Profile Id: e2e (Active: false, Source: pom)

To see what a profile changes without running the build, compare mvn help:effective-pom and mvn help:effective-pom -P prod. A profile passed in -P but not found in any POM is reported by Maven 3.9.x with a warning like The requested profile "prd" could not be activated because it does not exist. — a common typo in CI scripts that does not fail the build.

Maven Profiles vs Spring Profiles

Maven profiles and Spring profiles are different mechanisms that share a name: a Maven profile works at build time and changes the artifact itself, while a Spring profile works when the application starts and selects a configuration inside the same artifact. They can be linked, but one should not replace the other.

Aspect Maven profile Spring profile
When it works During the build (mvn package) When the application starts
Where it is described <profiles> in pom.xml or settings.xml application-{profile}.properties, the @Profile annotation
How to switch it on -P prod, an <activation> condition spring.profiles.active=prod, the SPRING_PROFILES_ACTIVE variable
Result A separate artifact for each environment One artifact, the configuration is chosen on site

For environment settings (addresses, logins, log levels), Spring profiles or environment variables are usually more convenient: the same jar passes the test environment and goes to production. Maven profiles are kept for what really changes the build: the set of modules, check plugins, dependencies for a specific JDK.

Why Not to Keep Passwords in Profiles

Passwords, tokens and other secrets should not go into the profiles of pom.xml: the file is stored in Git, and mvn deploy publishes it to the artifact repository together with all its profiles. If the secret is also substituted into resources by filtering, it ends up inside the built jar.

Costly slip: a prod profile with a password ends up in the repository

The line <db.password>secret</db.password> in the prod profile is visible to everyone with access to Git and to everyone who downloads your POM from Nexus or Maven Central. Removing it from the Git history and from published versions is much harder than never adding it. The application should get the database password at startup (an environment variable, a secrets store), and the credentials for Maven repositories should come from <servers> in settings.xml or from CI secrets.

When Profiles Misbehave: Common Mistakes

Most problems with Maven profiles are silent: the build passes, but with the wrong settings. So whenever in doubt, first run help:active-profiles with the same options as the build.

Symptom Cause Fix
The default profile "disappeared" after adding -P activeByDefault switches off when any other profile of the same POM is active Activate by <property><name>!...</name></property> or list the profiles explicitly
A profile with a <property> condition does not activate, although the property is in <properties> In Maven 3 the condition sees only system properties and -D Pass the property with -D (-Denv=ci) or put it into .mvn/maven.config
The profile did not activate, the build passed, the log says could not be activated because it does not exist A typo in -P, or the profile is declared in another POM Check the id against help:all-profiles
A profile with two conditions in <activation> never fires Since Maven 3.2.2 all conditions are required, not just one Split it into two profiles or meet both conditions
A resource still contains the line ${db.url} The profile with the property is not active, or filtering is not enabled for the directory Check help:active-profiles and <filtering>true</filtering>
A property from the profile was not applied, although the profile is active The same property is passed with -D: a user property takes precedence Remove the -D from the command or the CI script
A source directory from the profile is ignored, Maven reports an unrecognized element sourceDirectory and other directories cannot be set in a profile (a warning or an error, depending on the version) Set the directory in the main <build> or add the sources with a plugin
Production runs a build that passed testing, but behaves differently The artifacts were built with different -P and differ in content Move environment settings out of the build into the run-time configuration

Frequently Asked Questions

How do I enable a Maven profile in IntelliJ IDEA?

In the Maven tool window, expand the Profiles node and tick the profiles you need: IDEA passes them when importing the project and when running goals from that window. Profiles activated by a condition are detected by IDEA itself. In a Maven run configuration the list of profiles can also be set manually, like the -P option.

Can one profile activate another?

No. A property from the <properties> of a profile does not activate another profile: the <property> condition sees only system properties and -D (see the note in the section Maven Profile Activation). If two profiles are needed together, give them a common condition, for example one -Dci property, or list both: -P ci,it.

Do profiles of the parent POM work in modules?

They do, but more subtly than ordinary inheritance. If the condition of a profile declared in the parent is met while a module is being built, the profile is applied to the parent model, and the module receives the result: properties, dependencies, plugins. The profile itself does not belong to the module. Where a profile came from is shown by help:active-profiles (source), and the final model by help:effective-pom in the module directory.

Which wins: -Ddb.url or the property from a profile?

The value from the command line. The user property -Ddb.url=... overrides both the POM <properties> and the properties of the active profile, so a forgotten -D in a CI script can silently cancel a profile setting.

How can I see what a profile changes without running the build?

Compare the final models: mvn help:effective-pom -Doutput=base.xml and mvn help:effective-pom -P prod -Doutput=prod.xml, then any file comparison tool. The difference is exactly what the profile adds: properties, dependencies, plugins, modules.

Comments

Please log in or register to have a possibility to add comment.