Platform

The Quarkus extension ecosystem consists of the Quarkus extensions developed and maintained by the community, including the Quarkus core development team. While the Quarkus ecosystem (sometimes also referred to as the "Quarkus universe") includes all the Quarkus extensions ever developed, there is also a concept of a Quarkus platform.

What is the Quarkus Platform?

The Quarkus Platform is a curated set of extensions, tools, and configurations that make it easier to build, test, and deploy Java applications with Quarkus.

The fundamental promise of a Quarkus platform is any combination of the Quarkus extensions the platform consists of can be used in the same application without causing any conflict for each other. Each organization creating their Quarkus platform may establish their own criteria for the extensions to be accepted into the platform and the means to guarantee the compatibility between the accepted extensions.

How does the Quarkus Platform help?

When multiple extensions are used in the same project, a Quarkus platform BOM ensures:

  • version compatibility between the dependencies;

  • simplified dependency management;

  • easier upgrades.

Quarkus Platform Artifacts

Each Quarkus platform is defined with a few artifacts.

Quarkus Platform BOM

Each Quarkus Platform is expected to provide a Maven BOM artifact that

  • imports a chosen version of io.quarkus:quarkus-bom (the platform BOM may be flattened at the end, but it has to be based on some version of io.quarkus:quarkus-bom)

  • includes all the Quarkus extension artifacts (the runtime and the deployment ones) the platform consists of

  • includes all the necessary third-party artifacts that align the transitive dependency versions of the platform extensions to guarantee compatibility between them

  • includes the platform JSON descriptor artifact

  • possibly includes the platform configuration properties artifacts

Quarkus applications that want to include extensions from a Quarkus platform will be importing the Quarkus platform BOM.

Quarkus Platform Descriptor

Quarkus platform descriptor is a JSON artifact that provides information about the platform and its extensions to the Quarkus tools. E.g. https://code.quarkus.io/ and the Quarkus command line tools consult this descriptor to list, add and remove extensions to/from the project on user’s request. This artifact is also used as a Quarkus platform identifier. When Quarkus tools need to identify the Quarkus platform(s) used in the project, they will analyze the dependency version constraints of the project (the effective list of the managed dependencies from the dependencyManagement section in Maven terms) looking for the platform descriptor artifact(s) among them. Given that the platform descriptors are included into the Quarkus platform BOMs, every Quarkus application will inherit the platform descriptor artifact from the imported platform BOM(s) as a dependency version constraint (managed dependency in Maven terms).

To be able to easily identify Quarkus platform descriptors among the project’s dependency constraints, the platform descriptor Maven artifact coordinates should follow the following naming convention:

  • the groupId of the descriptor artifact should match the groupId of the corresponding Quarkus Platform BOM;

  • the artifactId of the descriptor artifact should be the artifactId of the corresponding Quarkus Platform BOM with the -quarkus-platform-descriptor suffix;

  • the classifier of the descriptor artifact should match the version of the corresponding Quarkus Platform BOM;

  • the type of the descriptor artifact should be json;

  • the version of the descriptor artifact should match the version of the corresponding Quarkus Platform BOM.

As a string it will look like <platform-bom-groupId>:<platform-bom-artifactId>-quarkus-platform-descriptor:<platform-version>:json:<platform-version>

E.g. the coordinates of the descriptor for the Quarkus BOM io.quarkus.platform:quarkus-bom::pom:1.2.3 will be io.quarkus.platform:quarkus-bom-quarkus-platform-descriptor:1.2.3:json:1.2.3. And for a custom Quarkus platform defined with BOM org.acme:acme-bom::pom:555 it will be org.acme:acme-bom-quarkus-platform-descriptor:555:json:555.

The classifier matching the version of the platform may look confusing at first. But this is what turns the descriptor into a true "fingerprint" of the platform. In both Maven and Gradle, the effective set of the dependency version constraints (or the managed dependencies) is obtained by merging all the imported BOMs and version constraints specified individually in the current project and also its parent(s). The artifact classifier is a part of the dependency ID, which could be expressed as groupId:artifactId:classifier:type. Which means that if a project imports a couple of BOMs, e.g. org.apple:apple-bom::pom:1.0 and org.orange:orange-bom::pom:1.0, and each of these two BOMs imports a different version io.quarkus.platform:quarkus-bom::pom, the Quarkus tools will be able to detect this fact and make the user aware of it, since it might not be a safe combination. If the descriptor artifact didn’t include the classifier containing the version of the platform then the tools wouldn’t be able to detect a potentially incompatible mix of different versions of the same platform in the same project.

The platform descriptor will normally be generated using a Maven plugin, e.g.

<plugin>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-platform-descriptor-json-plugin</artifactId>
  <version>${quarkus.version}</version> (1)
  <executions>
    <execution>
      <phase>process-resources</phase>
      <goals>
        <goal>generate-extensions-json</goal> (2)
      </goals>
    </execution>
  </executions>
  <configuration>
    <bomGroupId>${quarkus.platform.group-id}</bomGroupId> (3)
    <bomArtifactId>${quarkus.platform.artifact-id}</bomArtifactId> (4)
    <bomVersion>${quarkus.platform.version}</bomVersion> (5)
    <overridesFile>${overridesfile}</overridesFile> (6)
    <resolveDependencyManagement>true</resolveDependencyManagement> (7)
  </configuration>
</plugin>
1 the version of the quarkus-platform-descriptor-json-plugin
2 generate-extensions-json is the goal generating the platform descriptor
3 the groupId of the platform BOM
4 the artifactId of the platform BOM
5 the version of the platform BOM
6 this parameter is optional, it allows to override some metadata from the Quarkus extension descriptors found in every runtime extension artifact from which the platform descriptor is generated
7 this parameter is also optional and defaults to false. It has to be set to true in case the platform BOM is not generated and is not flattened. Which for example is the case for io.quarkus:quarkus-bom.

Quarkus Platform Properties

A Quarkus platform may provide its own default values for some configuration options.

Quarkus is using SmallRye Config for wiring application configuration. A Quarkus platform may be used as another source of configuration in the hierarchy of the configuration sources dominated by the application’s application.properties.

To provide platform-specific defaults, the platform needs to include a dependency version constraint in its BOM for a properties artifact whose coordinates follow the following naming convention:

  • the groupId of the properties artifact should match the groupId of the corresponding Quarkus Platform BOM;

  • the artifactId of the properties artifact should be the artifactId of the corresponding Quarkus Platform BOM with the -quarkus-platform-properties suffix;

  • the classifier of the descriptor artifact should be left empty/null;

  • the type of the descriptor artifact should be properties;

  • the version of the descriptor artifact should match the version of the corresponding Quarkus Platform BOM.

The properties artifact itself is expected to be a traditional properties file that will be loaded into an instance of java.util.Properties class.

At this point, platform properties are only allowed to provide the default values for a restricted set of configuration options. The property names in the platform properties file must be prefixed with the platform. suffix.

Extension developers that want to make their configuration options platform-specific should set their default values to properties that start with the platform. suffix. Here is an example:

package io.quarkus.deployment.pkg;

@ConfigRoot(phase = ConfigPhase.BUILD_TIME)
@ConfigMapping(prefix = "quarkus")
public interface NativeConfig {

    /**
     * The docker image to use to do the image build
     */
    @WithDefault("${platform.quarkus.native.builder-image}")
    String builderImage();
}

In this case the default value for quarkus.native.builder-image will be provided by the platform. The user will still be able to set the desired value for quarkus.native.builder-image in its application.properties, of course. But in case it’s not customized by the user, the default value will be coming from the platform properties. A platform properties file for the example above would contain:

platform.quarkus.native.builder-image=quay.io/quarkus/ubi9-quarkus-mandrel-builder-image:jdk-21

Starting with Quarkus 3.19+, the builder image used to build the native executable is based on UBI 9. It means that the native executable produced by the container build will be based on UBI 9 as well. So, if you plan to build a container, make sure that the base image in your Dockerfile is compatible with UBI 9. The native executable will not run on UBI 8 base images.

For example to switch back to an UBI8 builder image you can use:

platform.quarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21

You can see the available tags for UBI8 here and for UBI9 here (UBI 9))

There is also a Maven plugin goal that validates the platform properties content and its artifact coordinates and also checks whether the platform properties artifact is present in the platform’s BOM. Here is a sample plugin configuration:

<plugin>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-platform-descriptor-json-plugin</artifactId>
  <version>${quarkus.version}</version>
  <executions>
    <execution>
      <phase>process-resources</phase>
      <goals>
        <goal>platform-properties</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Merging Quarkus Platform Properties

In case an application is importing more than one Quarkus platform and those platforms include their own platform properties artifacts, the content of those platform properties artifacts will be merged to form a single set of properties that will be used for the application build. The order in which the properties artifacts are merged will correspond to the order in which they appear in the list of dependency version constraints of the application (in the Maven terms that will correspond to the effective list of application’s managed dependencies, i.e. the flattened managedDependencies POM section).

The content of the properties artifacts found earlier will dominate over those found later among the application’s dependency constraints!

That means if a platform needs to override a certain property value defined in the platform it is based on, it will need to include its platform properties artifact into the managedDependencies section of its BOM before importing the base platform.

For example, let’s assume org.acme:acme-quarkus-bom platform extends the io.quarkus:quarkus-bom platform by importing its BOM. In case, the org.acme:acme-quarkus-bom platform were to override certain properties defined in the io.quarkus:quarkus-bom-quarkus-platform-properties included in the io.quarkus:quarkus-bom, the org.acme:acme-quarkus-bom would have to be composed as

  <!-- skipped content -->

  <artifactId>acme-quarkus-bom</artifactId>
  <name>Acme - Quarkus - BOM</name>
  <packaging>pom</packaging>

  <dependencyManagement>
    <dependencies>
      <!-- Acme Quarkus platform properties -->
      <dependency>
        <groupId>org.acme</groupId>
        <artifactId>acme-quarkus-bom-quarkus-platform-properties</artifactId>
        <type>properties</type>
        <version>${project.version}</version>
      </dependency>

      <!-- The base Quarkus BOM -->
      <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-bom</artifactId>
        <version>${quarkus.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

  <!-- skipped content -->

That way, the org.acme:acme-quarkus-bom platform properties will appear before those provided by the io.quarkus:quarkus-bom properties and so will be dominating at build time.

Product attribution properties

A platform may declare that some of its members represent supported products (offerings). This information is consumed by CycloneDX SBOM generation to represent each product as a component and link the application artifacts that belong to it — see Product attribution in the CycloneDX guide.

The identity of a product is attached to an imported platform member BOM through platform properties whose names embed the member BOM’s coordinates:

platform.<member-bom-groupId>.<member-bom-artifactId>.cpe

The CPE of the product represented by the platform member. When present, a product component is generated in the SBOM for this member. This property alone is sufficient to enable product attribution for the member.

platform.<member-bom-groupId>.<member-bom-artifactId>.cpe-artifacts

An encoded map that, for each supported runtime extension artifact of the member, records the artifacts a consumer should attribute to the member’s CPE (the extension’s deployment artifact plus its resolved deployment dependency closure, aligned to the versions the platform ships). Keying the map by the runtime artifact lets a consumer look up the runtime artifacts it already has in its application model directly. The value is generated by the platform build tooling rather than written by hand, and is present only when the member declares both a CPE and an offering; when it is absent or empty, the product component is still emitted but without any attributed artifacts.

For example, for a platform member BOM org.acme:acme-camel-bom:

platform.org.acme.acme-camel-bom.cpe=cpe:2.3:a:acme:quarkus:3.40:*:*:*:*:*:*:*
platform.org.acme.acme-camel-bom.cpe-artifacts=<base64>

The generated product component can be further described with the following optional properties, using the same platform.<member-bom-groupId>.<member-bom-artifactId>. prefix: product-purl, product-name, product-version, product-description and product-type (defaults to framework). If product-purl is not set, the member BOM’s own pkg:maven PURL is used.

cpe-artifacts value format

The cpe-artifacts value is an encoding of a map that maps supported runtime extension artifacts to their corresponding original deployment dependencies.

Compression pipeline

The value is Base64 wrapping a DEFLATE-compressed UTF-8 rendering of the text format below:

  • Encode: build the text format → UTF-8 bytes → java.util.zip.Deflater with BEST_COMPRESSIONjava.util.Base64 (standard alphabet, no line breaks).

  • Decode: Base64.getDecoder().decode(value)java.util.zip.Inflater → UTF-8 string → parse the text format.

The compressed stream is a standard zlib/DEFLATE stream; the compression level is not needed for decompression.

Text format

Coordinates recur heavily across entries (deployment closures overlap, and even distinct coordinates share groupIds, versions and artifactId prefixes). To remove this repetition, the text has two sections separated by a line containing exactly --: a dictionary of every distinct coordinate (grouped so shared parts are written once) followed by the entries, which reference the dictionary by index.

@org.apache.camel.quarkus     ← groupId; the NEXT line is this group's common artifactId prefix
camel-quarkus-                ← positional prefix line (empty when there is no shared prefix)
=3.33.0                       ← version sub-block
core                          ← artifactId minus prefix → camel-quarkus-core     (classifier "", type jar)
support:linux-x86_64          ← camel-quarkus-support, classifier linux-x86_64, type jar
=3.20.0                       ← further versions reuse the same groupId + prefix
legacy                        ← camel-quarkus-legacy
@io.quarkus
quarkus-core                  ← prefix equals the only artifactId (singleton group)
=3.15.0
                              ← empty artifact line ⇒ artifactId equals the prefix exactly
--
1a[0,3,5]                     ← entry: runtime-key index, then delta-encoded dependency indices

In the dictionary section each line is self-identifying by its first character. Maven groupIds, artifactIds and versions never start with @ or =, so the dispatch is unambiguous:

Line Meaning

@groupId

Starts a group. The very next line is this group’s common artifactId prefix.

(line after @)

The group’s common artifactId prefix — positional, always present, and empty when the group has no shared prefix.

=version

Starts a version sub-block within the current group.

(anything else)

An artifact line (see below).

An artifact line is the artifactId with the group prefix stripped, optionally followed by :classifier and/or :type. Trailing default parts (empty classifier, jar type) are omitted. With prefix P, group G and version V:

Artifact line Reconstructed coordinate

rem

G:P+rem::jar:V (empty classifier, jar type)

rem:cls

classifier cls, type jar

rem::type

empty classifier, type type

rem:cls:type

classifier cls, type type

(empty line)

artifactId equals the prefix exactly (P), empty classifier, jar type

:cls

artifactId equals the prefix, classifier cls

Artifact lines take dictionary indices sequentially in the order they appear, starting at 0; the @, prefix and = lines consume no index.

In the entries section there is one line per runtime extension, of the form <key-index>[<Δ0>,<Δ1>,…​,<Δn>]:

  • <key-index> is the base-36 dictionary index of the runtime extension artifact (absolute).

  • Inside the brackets is the extension’s deployment-rooted dependency closure, as base-36 dictionary indices sorted ascending and delta-encoded: the first value is absolute, each subsequent value is the gap from the previous one (reconstruct by a running sum). An empty closure is <key-index>[].

For example, 1a[0,3,5] means: key = dictionary entry Integer.parseInt("1a", 36); dependencies = dictionary entries at absolute indices 0, 0+3=3, 3+5=8.

The output is reproducible: the dictionary is sorted by (groupId, version, artifactId, classifier, type), entries are ordered by their runtime key’s dictionary index, and dependency indices are ascending. Byte-identical output across builds additionally assumes a consistent JDK/zlib, as with any DEFLATE-based artifact.