AEM Guide

How an AEM Maven Project Actually Builds a Deployable Package

How the parent pom.xml and the core, ui.apps, ui.content, ui.config, and all modules of an AEM project combine to produce a single deployable package, and why the all module is the real deployment unit.

mavenarchetypecloud-managerci-cdpackaging

An AEM project generated with the AEM Project Archetype doesn’t produce a single artifact — it produces half a dozen. The usual confusion for someone new to an AEM codebase isn’t “what does Maven do,” it’s “which of all these pom.xml files is the one that actually gets deployed to the instance.” The short answer: almost none of them individually — what gets deployed is the output of one specific module, all, which assembles the rest.

The multimodule layout the archetype generates

Generating a project with com.adobe.aem:aem-project-archetype gives you a root parent (aggregator) pom.xml and several child modules, each with its own pom.xml and its own packaging type. The standard modules are:

The internals of ui.apps and ui.frontend deserve their own dedicated articles — what matters here is how all the modules fit together to produce the final package, not how each one works internally.

Two packaging types: bundle vs. content-package

core is the only module with plain jar packaging: it’s an OSGi bundle like any other, compiled and packaged with the usual bundle plugin. The remaining content-producing modules (ui.apps, ui.content, ui.config, all) use content-package packaging, built by the filevault-package-maven-plugin (org.apache.jackrabbit). This plugin replaced Adobe/Day’s older content-package-maven-plugin and is currently the only one supported on AEM as a Cloud Service.

Each content-package module also declares a packageType in the plugin configuration:

That classification isn’t cosmetic: Cloud Manager’s package validation uses it to reject builds where, for example, a package marked application touches /content paths it shouldn’t.

The all module: how the final package gets assembled

The all module’s pom.xml declares zip-type dependencies (or jar for core) on the other modules, and the filevault-package-maven-plugin configuration uses <embeddeds> to specify the install path each one gets embedded at inside the container package. This is the current approach; the older <subPackages> mechanism is deprecated.

<!-- all/pom.xml (fragment) -->
<plugin>
  <groupId>org.apache.jackrabbit</groupId>
  <artifactId>filevault-package-maven-plugin</artifactId>
  <extensions>true</extensions>
  <configuration>
    <group>com.myproject</group>
    <packageType>container</packageType>
    <embeddeds>
      <embedded>
        <groupId>com.myproject</groupId>
        <artifactId>myproject.core</artifactId>
        <type>jar</type>
        <target>/apps/myproject-packages/application/install</target>
      </embedded>
      <embedded>
        <groupId>com.myproject</groupId>
        <artifactId>myproject.ui.apps</artifactId>
        <type>zip</type>
        <target>/apps/myproject-packages/application/install</target>
      </embedded>
      <embedded>
        <groupId>com.myproject</groupId>
        <artifactId>myproject.ui.content</artifactId>
        <type>zip</type>
        <target>/apps/myproject-packages/content/install</target>
      </embedded>
    </embeddeds>
  </configuration>
</plugin>

Each <embedded> needs a matching <dependency> in the same pom.xml, pointing at the sibling module’s artifact version (normal in a Maven reactor, where ${project.version} keeps every module in sync).

Why this matters: a single deployable artifact

Running mvn clean install on the all module produces a single installable .zip. Instead of manually uploading four or five packages in the right order to Package Manager, or coordinating multiple steps in a deployment pipeline, the Cloud Manager pipeline (or any direct deployment script) uploads and activates one package. That single package is what actually defines which version of code and content is running on an AEM instance at any given moment.

This is also what makes the build deterministic: the root aggregator lists modules under <modules>, but it’s the dependency graph declared in each pom.xml — not the order of that list — that Maven uses to compute the reactor’s actual build order.

Where this comes up