Publishing build artifacts on Maven Central

December 11, 2025 ยท View on GitHub

This document provides information on how to configure your Java SDK project to successfully publish build artifacts (jars, pom.xml files, etc.) on the Maven Central public maven artifact repository.

Table of Contents

Overview

This document will assist you in configurating your Java SDK project to publish artifacts on Maven Central.

These Sonatype documents should be considered to be the authoritative source of information regarding the proper steps to be followed for publishing artifacts on Maven Central:

Note that the previous publishing process via the OSSRH service (aka Sonatype's "legacy" process) has reached end of life, so now all IBM Cloud Java SDK projects must use the new process.

The intent of this document is to provide specific instructions to guide IBM Cloud Java SDK maintainers in performing the steps outlined in aforementioned guide.

1: Register a new Sonatype account

It is highly recommended that you use a functional ID to publish artifacts to Maven Central. If you do not already have a Sonatype account, you'll need to create one before proceeding.

To register a new account, follow the instructions here.

After you have established your Sonatype account, follow the instructions here to generate a user token suitable for publishing. After generating the token, be sure to save the username and password values associated with your token in a safe place (we'll use them later).

2: Using the com.ibm.cloud maven group id

You'll be publishing your artifacts using the already-existing maven group id com.ibm.cloud. Therefore, you do not need to create a new namespace as mentioned in the registration documentation.

Using the existing com.ibm.cloud maven group id provides some consistency for IBM Cloud customers wishing to use the artifacts produced by various Java SDK projects (i.e. IBM Cloud services).

A Java SDK project's main artifact (the "parent" project) should have an artifact id that represents the service category (e.g. platform-services), and each module within the project should have an artifact id that reflects the name of the service contained in that module (e.g. resource-controller, global-catalog, configuration-governance, etc.).

An artifact's group id, artifact id, and version form the maven coordinates needed by users to define a dependency on the artifact (e.g. com.ibm.cloud:global-catalog:1.3.1). Make sure the coordinates of each of your Java SDK project's artifacts are properly documented in your project's README.md file.

2.1: Request permissions to publish artifacts in com.ibm.cloud group

Follow the instructions here to request permissions to publish artifacts using the com.ibm.cloud maven group id. Specifically, you'll need to send an email to the "Central Support" team and provide the following info

  • request type - add "publish" permissions for the specified user
  • the username (the username associated with the account you created above)
  • the namespace (maven group id): com.ibm.cloud The request will be routed to the person that manages permissions for the com.ibm namespace (an IBMer).

3: Create a public/private key pair for signing artifacts

One of the requirements for publishing artifacts on Maven Central is that each file (jar, pom.xml, etc.) must be signed, and this requires the use of a public/private key pair.

Detailed instructions on how to create a new signing key can be found here.

In order to automatically sign artifacts during your automated CI builds, you'll need to export your key, encrypt it, and then add the encrypted file to your SDK project's git repository so that it can be used during your automated builds. See the instructions below on how to do this. This process depends on the CI/CD solution that you'll use in the project.

3.1: Generate a new public/private key pair

Use the detailed instructions to generate a new key pair:

gpg --gen-key

Use RSA for the kind and 2048-bit as the size of the key. Be sure to specify a non-trivial passphrase and save it somewhere secure (add it to your password manager, etc.). Omitting the passphrase makes it easier to forge artifact signatures.

This command creates the new key within your local keystore managed by the gpg command (called $HOME/.gnupg/pubring.kbx or something similar).

3.2: List keys

To list your keys:

gpg --list-keys

Example:

$ gpg --list-keys
/home/padams/.gnupg/pubring.kbx
-------------------------------
pub   rsa2048 2020-11-22 [SC] [expires: 2022-11-22]
      B265BB91D80124E463C63119EC4158B05D352E09
uid           [ultimate] Phillip C. Adams <phil_adams@us.ibm.com>
sub   rsa2048 2020-11-22 [E] [expires: 2022-11-22]

3.3: Publish your public key

In order to publish artifacts on Maven Central, you will need to publish your public key on a key server so that Sonatype can verify your artifacts before releasing them to Maven Central.

3.3.1 Via gpg command line

One way to publish your public key is to use the gpg command like this:

$ gpg --keyserver keys.openpgp.org --send-keys B265BB91D80124E463C63119EC4158B05D352E09
gpg: sending key EC4158B05D352E09 to hkp://keys.openpgp.org

Be sure to use your key's id in place of B265BB91D80124E463C63119EC4158B05D352E09 :).

3.3.2 Via key server web interface

Sometimes, the gpg command line method mentioned above will fail with a "connection timeout" or other networking-related error. Alternatively, you can publish your public key via the key server's web interface. To do this, you first need to export your public key like this:

$ gpg --armor --export B265BB91D80124E463C63119EC4158B05D352E09 > mykey.pub

Be sure to use your key's id in place of B265BB91D80124E463C63119EC4158B05D352E09 :).

Next, open the keyserver web interface then click the "upload" link below the search entry field. Next, click "Browse" and select the file to which you exported your public key in the previous step (e.g. mykey.pub), then click the "Upload" button.

3.3.3 Verify your key was published

Next, you can verify that your key was published. Open that page and enter the key id (either the full key id or the last 16 characters... e.g. B265BB91D80124E463C63119EC4158B05D352E09 or EC4158B05D352E09 from the example above, but be sure to use your own key id), then click Search.
Your public key should be returned as a result of the search.

Publishing your public key also allows users of your artifacts to verify them with the gpg command:

$ gpg --verify <jar_filename>.asc

3.4: Export your key

After following the steps above, you should have a new signing key (a public/private key pair) within your local keystore. This key will need to be exported, encrypted, and then added to your project's git repository so that your CI/CD build can import the key into the build agent's local keystore to allow the maven build running on that agent to automatically sign your artifacts with your signing key.

So, while we're dealing with the gpg-related steps here, we might as well export it now:

gpg --export-secret-key -a B265BB91D80124E463C63119EC4158B05D352E09 > signing.key

Of course, replace B265BB91D80124E463C63119EC4158B05D352E09 with your key's id (found in the output of the gpg --list-keys command above). You should now have a text file named signing.key that looks like this:

-----BEGIN PGP PRIVATE KEY BLOCK-----

<contents of private key>

-----END PGP PRIVATE KEY BLOCK-----

Next, we'll update your Java SDK project so that it can automatically sign and publish your build artifacts.

4: Modify your Java SDK project to publish artifacts on Maven Central

In this section, you will be making updates to your Java SDK project, so be sure to create a new feature branch from your current "master" (or "main") branch.

Note: If you created a new Java SDK project from the Java SDK Template repository on or after 02/06/2021, then your project should already have the changes outlined in this section of the document. This section is intended mainly for those that have created their project from an earlier version of the template repository and need to update their project to remove the bintray publishing step and replace it with a maven central publishing step.

4.1: Copy files from the Java SDK template repository

Copy the following files from the Java SDK Template repository into the build directory of your Java SDK project:

  • build/generateJavadocIndex.sh
  • build/publishJavadoc.sh
  • build/setMavenVersion.sh
  • build/setupSigning.sh

If you'll use Travis instead of GitHub Actions, add the following files too:

  • publishJavadoc.sh (Travis version)
  • build/.travis.settings.xml (Only if you are about to use Travis)

Next, within your Java SDK project, modify the build/generateJavadocIndex.sh file to reflect your Java SDK project (i.e. the title, h1 heading, <service-category>, and <github-repo-url>).

4.2: Update your .travis.xml file

Note: this section is only applicable to Travis. If you'll use other CI/CD solution - like GitHub Actions -, skip this and move to the next one.

Next, replace your project's .travis.yml file with the contents of .travis.yml.sdk from the Java SDK Template repository. If you have project-specific settings in your .travis.yml file, be sure to back up the file first, then copy any project-specific settings to your new .travis.yml file. You should end up with a new .travis.yml file in the root directory of your project. Within the .travis.yml file, remove the comments from the stages and jobs sections (there are instructions inside the file).

4.3: Add your signing key to your git repository.

Note: this section is only applicable to Travis. If you'll use other CI/CD solution - like GitHub Actions -, skip this and move to the next one.

In this step, you'll encrypt the signing.key file that you previously exported and add it to your git repo within the build directory.

Detailed instructions on how to encrypt a file and add it to your repository can be found here, so the instructions below will be kept brief:

$ cd <project-root>

# Copy your signing.key file to your project's "build" directory. 
# Replace "<dir>" with the name of the directory containing the signing.key file
# that you exported previously.
$ cp <dir>/signing.key ./build

$ travis login --github-token <your-public-github-token> --com

$ travis encrypt-file build/signing.key build/signing.key.enc --com

The travis encrypt-file command will display an openssl command as part of its output. Here is an example:

openssl aes-256-cbc -K $encrypted_4b7d603e7466_key -iv $encrypted_4b7d603e7466_iv -in build/signing.key.enc -out build/signing.key -d

Next, modify the openssl command found inside the build/setupSigning.sh script to reflect the names of the environment variables from the openssl command displayed by the travis encrypt-file command (or just copy the entire openssl command and replace the one in the script. In the example above, the environment variable names are $encrypted_4b7d603e7466_key and $encrypted_4b7d603e7466_iv. The travis encrypt-file command added these environment variables to your project's Travis build settings.

Finally, remove the build/signing.key file (remove only the plain-text file and keep the build/signing.key.enc file):

$ rm build/signing.key

4.4: Update the parent pom.xml

In this step, you'll modify your Java SDK project's parent pom.xml file (in the project root directory) to add the necessary configuration to allow your project to sign and publish your build artifacts. As a reminder, if you created your project from a recent version of the template repository, you likely already have these changes reflected in your parent pom.xml.

The most straightforward way to perform this step might be to "diff" your pom.xml file with the parent pom.xml in the Java SDK Template repository and apply the necessary changes, while retaining your project-specific settings. However, I'll attempt to summarize the changes here:

  • In the <properties> section, remove the <bintray-plugin-version> property, then add these properties:
<central-publish-plugin-version>0.8.0</central-publish-plugin-version>
<maven-gpg-plugin-version>3.1.0</maven-gpg-plugin-version>
  • Also in the <properties> section, remove these properties:

    • <bintray.org>
    • <bintray.repo>
    • <bintray.package.url>
  • Within the <modules> section, make sure that your examples module is listed last, after the coverage-reports module.

  • In the <repositories> section, remove the <repository> entry with id jcenter (if present).

  • In the <build>/<pluginManagement>/<plugins> section, remove the <plugin> entry for "bintray-maven-plugin", and add the following:

<plugin>
    <groupId>org.sonatype.central</groupId>
    <artifactId>central-publishing-maven-plugin</artifactId>
    <version>${central-publish-plugin-version}</version>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-gpg-plugin</artifactId>
    <version>${maven-gpg-plugin-version}</version>
</plugin>
  • In the <build>/<plugins> section, remove the <plugin> entry for "bintray-maven-plugin".

  • After the <developers> section (i.e. immediately before the file's closing </project> line), add the following <profiles> section:

    <profiles>

        <!-- "central" is used to deploy artifacts on maven central -->
        <profile>
            <id>central</id>

            <!-- For this profile, we'll get dependencies from maven central -->
            <repositories></repositories>

            <distributionManagement>
                <snapshotRepository>
                    <!-- We don't deploy snapshot releases -->
                </snapshotRepository>
                <repository>
                    <!-- This is where the nexus staging plugin will publish artifacts -->
                    <id>central</id>
                    <url>https://central.sonatype.com/repository/maven-snapshots/</url>
                </repository>
            </distributionManagement>

            <build>
                <plugins>
                    <plugin>
                        <groupId>org.sonatype.central</groupId>
                        <artifactId>central-publishing-maven-plugin</artifactId>
                        <extensions>true</extensions>
                        <configuration>
                            <publishingServerId>central</publishingServerId>
                            <autoPublish>true</autoPublish>
                        </configuration>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-gpg-plugin</artifactId>
                        <executions>
                            <execution>
                                <id>sign-artifacts</id>
                                <phase>verify</phase>
                                <goals>
                                    <goal>sign</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <gpgArguments>
                                <gpgArgument>--batch</gpgArgument>
                                <gpgArgument>--yes</gpgArgument>
                                <gpgArgument>--no-tty</gpgArgument>
                            </gpgArguments>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
            <properties>
                <!-- Configure the gpg plugin to use the env vars defined in the Travis build settings -->
                <gpg.keyname>${env.GPG_KEYNAME}</gpg.keyname>
                <gpg.passphrase>${env.GPG_PASSPHRASE}</gpg.passphrase>
            </properties>
        </profile>
    </profiles>

Note: if you already have a <profiles> section, then simply add the <profile> entry with id "central" to it.

4.5: Update the coverage-reports module pom.xml

In this step, you'll modify the modules/coverage-reports/pom.xml file to prevent the coverage-reports module from being published on maven central.

  • Add this <properties> section immediately before the <dependencies> section:
    <properties>
	<!-- There is no need to publish this module's artifacts on maven central -->
        <maven.deploy.skip>true</maven.deploy.skip>
        <skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo>
    </properties>
  • In the <build>/<plugins> section, remove the <plugin> entry with id "bintray-maven-plugin".

4.6: Update your build settings

4.6.1: For GitHub Actions

For setting up GitHub Actions, refer to the Using GitHub Actions instead of Travis-CI for your project's CI/CD part of this document while you are reading the instructions below.

4.6.2: For Travis

Open the Travis build settings page for your SDK project (i.e. https://travis-ci.com/github/IBM/<repo-name>), and add the following environment variables to your build configuration:

  • GH_TOKEN: the github access token for a user (preferably a functional ID) with push access to your project's git repository (used when pushing tags and commits).

  • CP_USERNAME: the username portion of the Sonatype user token that you created earlier.

  • CP_PASSWORD: the password portion of the Sonatype user token that you created earlier.

  • GPG_KEYNAME: this is the name/id of the signing key that will be used by the maven gpg plugin to sign your artifacts. The value used here should be the last 8 characters of the key name. For example, when I run gpg --list-keys, I see this output:

$ gpg --list-keys
/home/padams/.gnupg/pubring.kbx
-------------------------------
pub   rsa2048 2020-11-22 [SC] [expires: 2022-11-22]
      B265BB91D80124E463C63119EC4158B05D352E09
uid           [ultimate] Phillip C. Adams <phil_adams@us.ibm.com>
sub   rsa2048 2020-11-22 [E] [expires: 2022-11-22]

In this case, I would set the GPG_KEYNAME variable to 5D352E09.

  • GPG_PASSPHRASE: this is the passphrase associated with your signing key.

Note: when entering the values of environment variables, be sure to escape any special characters as instructed by the Travis build settings UI.

4.7: Commit your changes

After you have completed the above changes to your Java SDK project, you can commit the changes and push your feature branch, then open a pull request, etc. Feel free to tag Phil Adams as a reviewer on your pull request.

When committing your changes, be sure to use a commit message that will trigger a new release when your pull request is merged and semantic-release processes your PR's commits (e.g. git commit -m "feat(build): deploy artifacts to maven central" or something similar).

Do not merge your PR until the JIRA ticket you opened in step 2 is resolved (i.e. the Sonatype personnel have given your Sonatype account the necessary permission to publish artifacts).

5: Java SDK Build lifecycle overview

This section describes the various types of builds and build stages associated with the travis build configuration that you copied from the Java SDK Template repository.

Within the .travis.yml file, there are three main stages defined:

stages:
  - name: Build-Test
  - name: Semantic-Release
    if: branch = master AND type = push AND fork = false
  - name: Publish-Release
    if: tag IS present

When you push a feature branch or open a pull request, the resulting travis build will run only the Build-Test stage, which builds and unit tests the project.

When a pull request is merged into the master branch, the Build-Test stage is run, and (assuming that was successful), the Semantic-Release stage is then run. The Semantic-Release stage will run the semantic-release tool which will:

  • Analyze the commit messages associated with the commits that were merged into the master branch since the most recent tagged-release of the project to determine if a new version (tagged-release) of the project is warranted.
  • If a new version is needed, semantic-release will compile a changelog entry for the new version, perform some additional release management steps and will then push one or more commits back to the github repository along with a tag to represent the new version of the project.

The tag pushed by the semantic-release tool will trigger a "tagged-release" Travis build. This build will run the Build-Test stage, then the Publish-Release stage. The Publish-Release stage will then run two jobs in parallel:

  • the Publish-Javadoc job will produce javadocs from the project source, and then publish them on the git repository's gh-pages branch.
  • the Publish-To-Maven-Central job will publish the project's artifacts on Maven Central through the use of the nexus-staging-maven-plugin.

Appendix:

Using GitHub Actions instead of Travis-CI for your project's CI/CD

Due to occasional problems with the public Travis-CI service, some open-source IBM SDK projects have migrated from the public Travis-CI service to GitHub Actions for performing project builds. You can see an example of this in the IBM Cloud Platform Services Java SDK. If you choose to use GitHub Actions for your Java SDK project's CI/CD, this section will provide notes to help you adapt the instructions above which refer to Travis-CI.

  1. For an example workflow, see https://github.com/IBM/platform-services-java-sdk/tree/main/.github/workflows. There, you'll find:
  • build.yaml - performs build and unit test
  • publish.yaml - publishes new releases of the project
  1. When using GH Actions, you won't need a .travis.yml file, so that can be removed from your project.

  2. Instead of defining various secrets as environment variables in the Travis build settings, you will instead define these values as "Actions Secrets" (Settings->Secrets and variables->Actions within your git repo). Use the same names as those mentioned in the Travis instructions above: GH_TOKEN, GPG_KEYNAME, GPG_PASSPHRASE, CP_USERNAME, and CP_PASSWORD. Note that the specific names mentioned here will work in conjunction with the example publish.yaml workflow file mentioned above. If you choose to use different names for your secrets, just make sure that your workflow is adjusted accordingly.

  3. When using GH Actions, add your signing key to your git repository simply by adding the contents of the signing key file as a secret named GPG_PRIVATE_KEY, rather than committing an encrypted version of the signing key file to your git repo as you would when using Travis-CI.

References: