Why Maven Central Matters
You've built an amazing Java library. It solves real problems, the code is clean, and you're ready to share it with the world. But there's one crucial step between your local repository and global adoption: publishing to Maven Central.
Maven Central isn't just another package repository: it's the canonical repository for Java and JVM libraries. With over 10 million artifacts and billions of downloads monthly, it's where the entire Java ecosystem comes to find dependencies. When your library is on Maven Central, developers can add it to their projects with a single dependency declaration. No custom repositories, no manual downloads, no friction.
In this guide, I'll walk you through the entire process of publishing to Maven Central, from initial setup to your first release. Whether you're publishing your first library or your tenth, this guide will help you navigate the journey.
Step 1: Create Your Sonatype Account
Maven Central is operated by Sonatype, and they've recently modernized the onboarding process. You'll need to create an account on their new platform.
- Go to https://central.sonatype.com
- Click "Sign up" (you can use GitHub, Google, or email)
- Verify your email address if using email registration
Step 2: Claim Your Namespace
Namespace ownership is fundamental to publishing on Maven Central. Your namespace (groupId) serves as the unique identifier for all your published artifacts, and you must demonstrate legitimate ownership before you can publish. The Central Portal offers two primary verification methods, each suited to different use cases.
Method 1: DNS-Based Verification (For Domain Owners)
If you own or maintain a registered domain name, you can leverage the Domain Name System in reversed format for your namespace. This approach mirrors Java's package naming convention and provides the most flexibility for organizational publishing.
How It Works:
The namespace uses your domain name in reverse order, allowing you to create as many subsections as needed for organizational purposes.
Examples:
- Domain: example.com → Namespace: com.example
- Domain: subdomain.example.com → Namespace: com.example
- Domain: my-domain.com → Namespace: com.my-domain
Once verified, you can utilize any groupId starting with your reversed domain and extend it with additional subsections:
- com.example.domain
- com.example.test
- com.example.utilities.logging
Important Considerations:
Exact Reversal: The groupId must reverse the domain name exactly as it appears, preserving hyphens and special characters even if they would produce invalid Java package names. This is acceptable and expected, your Java package names need not match your groupId.
Ownership Requirement: You must have control over the domain to add DNS verification records.
Verification Process:
- Navigate to "Namespaces" in the left sidebar
- Click "Add Namespace"
- Enter your reversed domain namespace (e.g., com.example)
- Select "DNS Verification" as your verification method
- Copy the provided TXT record from the portal
- Add this TXT record to your domain's DNS configuration
- Return to the portal and click "Verify Namespace"
- Verification typically completes within minutes once DNS propagates
Method 2: Code Hosting Service Verification (Personal Namespace)
For individual developers and open-source projects hosted on popular code hosting platforms, Maven Central provides a streamlined verification process using your platform identity. This method is ideal for personal libraries and projects without a custom domain.
Supported Platforms:
Code Hosting Service | Username Pattern | Example Namespace |
---|---|---|
GitHub | myusername |
io.github.myusername |
GitLab | myusername |
io.gitlab.myusername |
Gitee | myusername |
io.gitee.myusername |
Bitbucket | myusername |
io.bitbucket.myusername |
How It Works:
The namespace format follows the pattern io.{platform}.{username}
, where the username matches your account on the respective platform. This creates a globally unique namespace tied to your platform identity.
Verification Process:
- Navigate to "Namespaces"
- Click "Add Namespace"
- Enter your platform-based namespace (e.g.,
io.github.myusername
) - Select your code hosting service as the verification method
- The portal will prompt you to create a temporary public repository with a specific verification name
- Create the repository on your platform with the exact name provided
- Return to the portal and click "Verify Namespace"
- Verification is typically instant once the repository is detected
Step 3: Generate GPG Keys
Maven Central enforces a strict security requirement: all published artifacts must be cryptographically signed using GPG (GNU Privacy Guard). This digital signature ensures artifact integrity and authenticity, protecting the entire Java ecosystem from tampering and unauthorized modifications.
3.1 Installing GPG
Before generating keys, you need to ensure GPG is installed on your system.
For macOS
Using Homebrew (recommended):
brew install gnupg
Using MacPorts:
sudo port install gnupg2
Verify installation:
gpg --version
For Linux
Debian/Ubuntu:
sudo apt-get update
sudo apt-get install gnupg
Fedora/RHEL/CentOS:
sudo dnf install gnupg2
Arch Linux:
sudo pacman -S gnupg
Verify installation:
gpg --version
For Windows
Option 1: Using Gpg4win (Recommended)
- Download Gpg4win from https://gpg4win.org/download.html
- Run the installer and follow the installation wizard
- Add GPG to your PATH if not done automatically
Option 2: Using Chocolatey
choco install gnupg
Option 3: Using Windows Subsystem for Linux (WSL)
sudo apt-get install gnupg
Verify installation (in Command Prompt or PowerShell):
gpg --version
3.2 Generating Your Key Pair
Once GPG is installed, generate a new key pair for signing your artifacts:
gpg --gen-key
Follow the interactive prompts to configure your key:
- Key type: Choose RSA and RSA (default)
- Key size: Use 2048 bits or higher (4096 recommended)
- Expiration: Set to 2-3 years (recommended) or no expiration
- Real name: Your name or organization name
- Email address: A professional email you control
- Comment: Optional (e.g., "Maven Central Signing Key")
- Passphrase: Create a strong passphrase
Critical: Your passphrase will be required for every release. Store it securely in a password manager—you cannot recover it if lost, and you'll need it throughout your publishing workflow.
Listing Your Keys
After generation, verify your keys:
gpg --list-keys
Output will look like:
pub rsa4096 2025-10-05 [SC] [expires: 2027-10-05]
1234567890ABCDEF1234567890ABCDEF12345678
uid [ultimate] John Doe (Maven Central Signing Key) <john.doe@example.com>
sub rsa4096 2025-10-05 [E] [expires: 2027-10-05]
The long hexadecimal string is your key ID. You can also use the last 8 characters as a short key ID.
3.3 Publishing Your Public Key
For Maven Central to verify your signatures, your public key must be published to a public key server:
# Using the full key ID
gpg --keyserver keys.openpgp.org --send-keys 1234567890ABCDEF1234567890ABCDEF12345678
# Or using the short key ID (last 8 characters)
gpg --keyserver keys.openpgp.org --send-keys 12345678
Note: It may take a few minutes for your key to propagate across key server networks.
Verifying Key Publication
Confirm your key is publicly accessible:
gpg --keyserver keys.openpgp.org --recv-keys YOUR_KEY_ID
If successful, you'll see:
gpg: key YOUR_KEY_ID: "John Doe (Maven Central Signing Key) <john.doe@example.com>" not changed
gpg: Total number processed: 1
gpg: unchanged: 1
Next Steps
With your GPG keys generated, published, and backed up, you're ready to configure your build tool to automatically sign artifacts during the publishing process. Your key ID and passphrase will be required in the build configuration covered in Step 4.
Step 4: Configure Your Build Tool
Configuring Maven for publishing to Maven Central requires understanding two distinct deployment workflows: release versions and snapshot versions. Each serves a different purpose in the development lifecycle and has different requirements.
Understanding Release vs Snapshot Versions
Release Versions (e.g., 1.0.0
, 2.3.1
, 1.5.0-beta1
):
- Immutable and permanent once published
- Require complete metadata, including developers, SCM, and license information
- Undergo full validation before publication
- Intended for production use and stable APIs
- Cannot be overwritten or deleted after publication
Snapshot Versions (e.g., 1.0.0-SNAPSHOT
, 2.3.1-SNAPSHOT
):
- Mutable and can be republished with the same version
- Require minimal metadata validation
- Used for development, testing, and continuous integration
- Not indexed in Maven Central search
- Must be explicitly enabled in your namespace configuration
Generating Authentication Tokens
Before configuring your build, you need to generate authentication tokens from the Central Portal:
- Log in to https://central.sonatype.com
- Click on your account name in the top right corner
- Select "View User Tokens" from the dropdown
- Click "Generate User Token"
- The portal will display your username and password token
- Save these immediately—the password token is only shown once
Important: These tokens are not your Sonatype login credentials. They are specifically generated for programmatic publishing and should be treated as API keys.
Enabling Snapshot Publishing (Optional)
If you plan to publish snapshot versions, you must enable this feature in your namespace:
- Log in to https://central.sonatype.com
- Navigate to "Namespaces"
- Select your verified namespace
- Click on its "dropdown menu"
- Click on "Enable Snapshots"
- Save the configuration
Without this setting enabled, snapshot deployments will fail with an authorization error.
Configuration for Release Versions
Release versions require comprehensive metadata and validation. This configuration ensures your artifacts meet all Maven Central requirements.
Maven POM Configuration
Add the following to your pom.xml
:
<project>
<groupId>io.github.yourusername</groupId>
<artifactId>your-library</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>Your Library Name</name>
<description>A comprehensive description of your library's purpose and functionality</description>
<url>https://github.com/yourusername/your-library</url>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>yourusername</id>
<name>Your Full Name</name>
<email>your.email@example.com</email>
<organization>Your Organization</organization>
<organizationUrl>https://yoursite.com</organizationUrl>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/yourusername/your-library.git</connection>
<developerConnection>scm:git:ssh://github.com:yourusername/your-library.git</developerConnection>
<url>https://github.com/yourusername/your-library/tree/main</url>
<tag>HEAD</tag>
</scm>
<build>
<plugins>
<!-- Central Publishing Plugin -->
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.9.0</version>
<extensions>true</extensions>
<configuration>
<publishingServerId>central</publishingServerId>
<autoPublish>true</autoPublish>
<waitUntil>published</waitUntil>
</configuration>
</plugin>
<!-- GPG Signing Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- Retrieves passphrase from settings.xml -->
<passphraseServerId>gpg.passphrase</passphraseServerId>
<!-- Required for gpg2 to avoid GUI prompts -->
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
<!-- Ensure correct GPG binary is used -->
<executable>gpg</executable>
</configuration>
</plugin>
<!-- Source Attachment Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Javadoc Generation Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.3</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- Optional: Configure Javadoc strictness -->
<doclint>none</doclint>
<quiet>true</quiet>
</configuration>
</plugin>
</plugins>
</build>
</project>
Plugin Explanations
Central Publishing Maven Plugin
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.9.0</version>
This is the modern publishing plugin that replaces the legacy Nexus staging plugin. It handles the entire deployment workflow to Maven Central.
Key Configuration Options:
-
publishingServerId
: References the server credentials insettings.xml
(must becentral
) -
autoPublish
: Whentrue
, automatically publishes after successful validation. Whenfalse
, requires manual approval in the Central Portal -
waitUntil
: Controls deployment behavior-
uploaded
: Waits until artifacts are uploaded -
validated
: Waits until validation completes -
published
: Waits until artifacts are fully published to Maven Central (recommended)
-
Maven GPG Plugin
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.1.0</version>
Signs all project artifacts (JAR, sources, javadoc, POM) with your GPG key during the verify phase.
Key Configuration Options:
-
passphraseServerId
: References the GPG passphrase stored insettings.xml
, avoiding hardcoded credentials -
gpgArguments
: Configures GPG behavior-
--pinentry-mode loopback
: Essential for non-interactive environments (CI/CD) and gpg2, prevents GUI password prompts
-
-
executable
: Specifies the GPG binary path. Usegpg2
if you have both versions installed
Maven Source Plugin
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.1</version>
Generates a JAR file containing your source code. Maven Central mandates source JARs for all releases.
Key Configuration:
-
jar-no-fork
: Creates the source JAR without forking a new Maven lifecycle, improving build performance
Maven Javadoc Plugin
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.3</version>
Generates comprehensive API documentation and packages it as a JAR. Maven Central requires Javadoc JARs for all releases.
Optional Configuration:
-
doclint
: Set tonone
to disable strict Javadoc validation (useful if you have legacy code with incomplete docs) -
quiet
: Reduces console output during generation
Maven Settings Configuration
Add the following to your ~/.m2/settings.xml
:
<settings>
<servers>
<!-- Central Portal Authentication -->
<server>
<id>central</id>
<username>YOUR_GENERATED_TOKEN_USERNAME</username>
<password>YOUR_GENERATED_TOKEN_PASSWORD</password>
</server>
<!-- GPG Passphrase -->
<server>
<id>gpg.passphrase</id>
<passphrase>YOUR_GPG_PASSPHRASE</passphrase>
</server>
</servers>
</settings>
Deploying a Release
Execute the deployment with a single command:
mvn clean deploy
This command will:
- Clean previous build artifacts
- Compile your code
- Run tests
- Generate source and Javadoc JARs
- Sign all artifacts with GPG
- Upload to Maven Central
- Validate the deployment
- Automatically publish (if
autoPublish
istrue
)
Verification: Monitor the deployment at https://central.sonatype.com/publishing/deployments
Configuration for Snapshot Versions
Snapshot versions are designed for rapid iteration during development. They have relaxed validation requirements and can be republished multiple times.
Maven POM Configuration for Snapshots
Snapshots require minimal metadata compared to releases:
<project>
<groupId>io.github.yourusername</groupId>
<artifactId>your-library</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- Minimal required metadata for snapshots -->
<name>Your Library Name</name>
<description>Development snapshot of Your Library</description>
<url>https://github.com/yourusername/your-library</url>
<build>
<plugins>
<!-- Central Publishing Plugin (same as releases) -->
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.9.0</version>
<extensions>true</extensions>
<configuration>
<publishingServerId>central</publishingServerId>
<autoPublish>true</autoPublish>
</configuration>
</plugin>
<!-- GPG Signing (same as releases) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
<configuration>
<passphraseServerId>gpg.passphrase</passphraseServerId>
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
<executable>gpg</executable>
</configuration>
</plugin>
<!-- Source and Javadoc plugins are optional for snapshots -->
<!-- Include them if you want consistent artifact structure -->
</plugins>
</build>
</project>
Key Differences for Snapshots
Relaxed Requirements:
- No
<developers>
section required - No
<scm>
section required - No
<licenses>
section required (though recommended) - Source and Javadoc JARs are optional
Version Format:
- Must end with
-SNAPSHOT
(e.g.,1.0.0-SNAPSHOT
,2.1.0-SNAPSHOT
) - Can be republished multiple times without changing the version number
Publication Behavior:
- Not indexed in Maven Central search
- Available immediately after upload
- Stored in a separate snapshot repository
- Automatically cleaned up after a retention period
Deploying a Snapshot
Deploy snapshots using the same command:
mvn clean deploy
Maven automatically detects the -SNAPSHOT
suffix and routes the deployment to the snapshot repository.
Consuming Published Snapshots
To use snapshots published by others (or your own), configure your project's pom.xml
or settings.xml
:
In Your Project's POM
<repositories>
<repository>
<id>central-portal-snapshots</id>
<name>Central Portal Snapshots</name>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
In Your Settings.xml (Global Configuration)
<settings>
<profiles>
<profile>
<id>central-snapshots</id>
<repositories>
<repository>
<id>central-portal-snapshots</id>
<name>Central Portal Snapshots</name>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>central-snapshots</activeProfile>
</activeProfiles>
</settings>
Configuration Options:
-
updatePolicy
: Controls how often Maven checks for snapshot updates-
always
: Check on every build (recommended for active development) -
daily
: Check once per day (default) -
interval:X
: Check every X minutes -
never
: Never check for updates
-
Additional Resources
For more detailed information and the latest updates, refer to the official Sonatype documentation:
- Central Portal Documentation: https://central.sonatype.org/register/central-portal/
- Publishing Guide: https://central.sonatype.org/publish/publish-guide/
- Maven Configuration: https://central.sonatype.org/publish/publish-maven/
The official documentation provides comprehensive coverage of edge cases, advanced configurations, and troubleshooting scenarios that may not be covered in this guide.
Note: Sonatype recently migrated from the old JIRA-based system to the new Central Portal at central.sonatype.com. If you find older tutorials mentioning JIRA tickets and issues.sonatype.org, that process is now deprecated. This guide reflects the current, streamlined process.
Questions or issues? Drop a comment below, and I'll do my best to help!
Top comments (0)