Maven archetypes
last modified November 8, 2026
In this article we show how to generate Maven projects from archetypes and how to write our own archetype. The examples use Maven 3.10, the Archetype plugin 3.4.1, and Java 25.
An archetype is a project template. It is distributed as a JAR file
in a Maven repository, like any other artifact, and contains the files of the
new project together with a descriptor. The archetype:generate
goal downloads the archetype, asks for the values of its properties, such as
the groupId and artifactId, and writes a new project
into the current directory.
Archetypes are useful for starting projects that follow the same conventions: the same Java version, test framework, plugin versions, and directory layout. Many frameworks publish archetypes, and a team can publish its own.
Listing archetypes
Without parameters, archetype:generate runs in interactive mode
and lists all archetypes from the Maven Central catalog, which has thousands
of entries. The -Dfilter option narrows the list; its value is
matched against groupId:artifactId. Here we list the archetypes
published by the Maven project.
The plugin then waits for a number or a further filter; we press
Ctrl+C to leave. Several of these archetypes are
outdated. The ones still in use are maven-archetype-quickstart
for a Java application or library, maven-archetype-webapp for a
web application, maven-archetype-plugin for a Maven plugin, and
maven-archetype-archetype for a new archetype.
$ mvn archetype:generate -Dfilter=org.apache.maven.archetypes:
Choose archetype:
1: remote -> org.apache.maven.archetypes:maven-archetype-archetype (An archetype which contains a sample archetype.)
2: remote -> org.apache.maven.archetypes:maven-archetype-j2ee-simple (An archetype which contains a simplified sample J2EE application.)
3: remote -> org.apache.maven.archetypes:maven-archetype-marmalade-mojo (-)
4: remote -> org.apache.maven.archetypes:maven-archetype-mojo (An archetype which contains a sample a sample Maven plugin.)
5: remote -> org.apache.maven.archetypes:maven-archetype-plugin (An archetype which contains a sample Maven plugin.)
6: remote -> org.apache.maven.archetypes:maven-archetype-plugin-site (An archetype which contains a sample Maven plugin site. This archetype can be layered upon an
existing Maven plugin project.)
7: remote -> org.apache.maven.archetypes:maven-archetype-portlet (An archetype which contains a sample JSR-268 Portlet.)
8: remote -> org.apache.maven.archetypes:maven-archetype-profiles (-)
9: remote -> org.apache.maven.archetypes:maven-archetype-quickstart (An archetype which contains a sample Maven project.)
10: remote -> org.apache.maven.archetypes:maven-archetype-simple (An archetype which contains a simple Maven project.)
11: remote -> org.apache.maven.archetypes:maven-archetype-site (An archetype which contains a sample Maven site which demonstrates some of the supported document types like
APT, XDoc, and FML and demonstrates how to i18n your site. This archetype can be layered
upon an existing Maven project.)
12: remote -> org.apache.maven.archetypes:maven-archetype-site-simple (An archetype which contains a sample Maven site.)
13: remote -> org.apache.maven.archetypes:maven-archetype-site-skin (An archetype which contains a sample Maven Site Skin.)
14: remote -> org.apache.maven.archetypes:maven-archetype-webapp (An archetype which contains a sample Maven Webapp project.)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains):
...
The quickstart archetype
In batch mode, we pass all values as properties and add
-DinteractiveMode=false, or the -B option, so that
Maven does not ask anything. The archetypeGroupId defaults to
org.apache.maven.archetypes, so we can omit it for the
quickstart archetype.
Always give -DarchetypeVersion. Without it, the plugin uses
version 1.0 of the quickstart archetype, which generates a project with
JUnit 3. Version 1.5 has two properties of its own:
javaCompilerVersion, which defaults to 17, and
junitVersion, which defaults to 5.11.0. The
package property defaults to the groupId, and the
version to 1.0-SNAPSHOT.
The generated project builds right away. Besides the sources, it contains the
.mvn directory with empty maven.config and
jvm.config files; they mark the root of the project and can hold
default command line and JVM options. The generated POM pins the versions of
all lifecycle plugins, but these are the versions from the time the archetype
was released, 2024, so it is worth updating them.
$ mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart \
-DarchetypeVersion=1.5 -DgroupId=com.example -DartifactId=demo \
-DjavaCompilerVersion=25 -DjunitVersion=5.14.4 \
-DinteractiveMode=false
[INFO] Parameter: groupId, Value: com.example
[INFO] Parameter: artifactId, Value: demo
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.example
[INFO] Parameter: packageInPathFormat, Value: com/example
[INFO] Parameter: junitVersion, Value: 5.14.4
[INFO] Parameter: javaCompilerVersion, Value: 25
[INFO] Project created from Archetype in dir: /home/user/demo
[INFO] BUILD SUCCESS
...
$ tree -a demo
demo
├── .mvn
│ ├── jvm.config
│ └── maven.config
├── pom.xml
└── src
├── main
│ └── java
│ └── com
│ └── example
│ └── App.java
└── test
└── java
└── com
└── example
└── AppTest.java
10 directories, 5 files
$ cd demo
$ mvn package
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.034 s -- in com.example.AppTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] Building jar: /home/user/demo/target/demo-1.0-SNAPSHOT.jar
[INFO] BUILD SUCCESS
...
Archetype templates and JUnit 6
The files of an archetype are Velocity templates. The quickstart POM template
decides which JUnit dependencies to add by looking at the start of the
junitVersion value.
#macro( junit $ver )
#if ( $ver.startsWith("4.") )
<dependencies>
...
#elseif ( $ver.startsWith("5.") )
<dependencyManagement>
...
#end
#end
The template knows only JUnit 4 and JUnit 5. With
-DjunitVersion=6.1.3, the generation succeeds, but the POM gets
no test dependency at all, while the generated test class still uses the
JUnit Jupiter API. The build fails when it compiles the tests. With
quickstart 1.5, use a 5.x version and change the BOM version in the POM to
6.x afterwards, or use your own archetype.
$ grep -c junit pom.xml 0 $ mvn test [ERROR] /home/user/demo/src/test/java/com/example/AppTest.java:[3,36] package org.junit.jupiter.api does not exist [ERROR] /home/user/demo/src/test/java/com/example/AppTest.java:[5,29] package org.junit.jupiter.api does not exist [ERROR] BUILD FAILURE ...
Archetype structure
A custom archetype is a Maven project with the maven-archetype
packaging. Our greeter archetype generates a Java 25 console application with
a JUnit 6 test.
The archetype-resources directory holds the files of the
generated project, archetype-metadata.xml describes them, and the
projects directory contains integration tests of the archetype.
The archetype has the following layout:
greeter-archetype
├── pom.xml
└── src
├── main/resources
│ ├── META-INF/maven/archetype-metadata.xml
│ └── archetype-resources
│ ├── pom.xml
│ ├── README.md
│ └── src
│ ├── main/java/App.java
│ └── test/java/AppTest.java
└── test/resources/projects/basic
├── archetype.properties
└── goal.txt
The archetype POM
The POM of the archetype project is short.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>greeter-archetype</artifactId>
<version>1.0.0</version>
<packaging>maven-archetype</packaging>
<description>Creates a Java 25 console application with JUnit 6</description>
<build>
<extensions>
<extension>
<groupId>org.apache.maven.archetype</groupId>
<artifactId>archetype-packaging</artifactId>
<version>3.4.1</version>
</extension>
</extensions>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-archetype-plugin</artifactId>
<version>3.4.1</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
The maven-archetype packaging is not built into Maven; it is
provided by the archetype-packaging build extension. The
extension binds the Archetype plugin goals to the lifecycle: the
jar goal packages the archetype, the
integration-test goal tests it, and the
update-local-catalog goal registers it in the local catalog on
install. The description is shown in archetype lists.
The archetype descriptor
The archetype-metadata.xml file declares the properties of the
archetype and the files it generates.
<?xml version="1.0" encoding="UTF-8"?>
<archetype-descriptor
xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.1.0 https://maven.apache.org/xsd/archetype-descriptor-1.1.0.xsd"
name="greeter">
<requiredProperties>
<requiredProperty key="greeting">
<defaultValue>Hello</defaultValue>
</requiredProperty>
<requiredProperty key="javaVersion">
<defaultValue>25</defaultValue>
<validationRegex>\d+</validationRegex>
</requiredProperty>
</requiredProperties>
<fileSets>
<fileSet filtered="true" packaged="true">
<directory>src/main/java</directory>
</fileSet>
<fileSet filtered="true" packaged="true">
<directory>src/test/java</directory>
</fileSet>
<fileSet filtered="true">
<directory></directory>
<includes>
<include>README.md</include>
</includes>
</fileSet>
</fileSets>
</archetype-descriptor>
The properties groupId, artifactId,
version, and package are always available. Our
archetype adds two more.
<requiredProperty key="greeting">
<defaultValue>Hello</defaultValue>
</requiredProperty>
A required property with a default value does not have to be given; the default is used. A required property without a default must be given on the command line in batch mode.
<validationRegex>\d+</validationRegex>
The validationRegex element checks the value. The check is done
only when the value is typed in interactive mode; in batch mode, an invalid
value such as -DjavaVersion=latest passes unchecked into the
generated POM.
<fileSet filtered="true" packaged="true">
<directory>src/main/java</directory>
</fileSet>
A file set selects files from archetype-resources. The
filtered attribute means that the files are processed as
Velocity templates. The packaged attribute means that the files
are placed into the directory of the package: App.java ends up
in src/main/java/org/acme/App.java for the
org.acme package.
<fileSet filtered="true">
<directory></directory>
<includes>
<include>README.md</include>
</includes>
</fileSet>
An empty directory refers to the root of the generated project. The POM template does not need a file set; it is always processed.
The templates
The templates use the ${name} syntax to insert property values.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<version>${version}</version>
<properties>
<maven.compiler.release>${javaVersion}</maven.compiler.release>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>6.1.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.16.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.6.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.5.1</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
The POM template fills in the coordinates and the Java release, and sets up JUnit 6 with its BOM and current plugin versions.
package ${package};
public class App {
static String greet(String name) {
return "${greeting}, " + name + "!";
}
public static void main(String[] args) {
String name = args.length > 0 ? args[0] : "${artifactId}";
System.out.println(greet(name));
}
}
The application greets the name given as an argument; without one, it
greets the name of the project. The greeting property becomes
part of the source code.
package ${package};
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class AppTest {
@Test
void greetsByName() {
assertEquals("${greeting}, Maven!", App.greet("Maven"));
}
}
The test uses the same property, so it matches the generated code.
Velocity treats $ and # specially. Every
${name} that matches an archetype property is replaced, so a
template that must produce a literal Maven expression such as
${version} has to escape it. The convention, also used by the
plugin itself, is a helper variable: #set( $symbol_dollar = '$' )
at the top of the file and ${symbol_dollar}{version} in the
text.
Testing the archetype
Each directory under src/test/resources/projects is one test
case. The archetype.properties file holds the values for the
generation.
groupId=org.acme artifactId=basic version=0.1.0 package=org.acme.basic greeting=Hi javaVersion=25
The goal.txt file names the goals run on the generated project;
ours contains a single word, package. So the test generates a
project and builds it, including its unit test.
The install phase runs the integration test, installs the
archetype into the local repository, and adds it to the local catalog in
~/.m2/repository/archetype-catalog.xml. Archetypes from the local
catalog are listed by mvn archetype:generate -DarchetypeCatalog=local.
$ mvn install
[INFO] --- resources:3.3.1:resources (default-resources) @ greeter-archetype ---
[INFO] --- resources:3.3.1:testResources (default-testResources) @ greeter-archetype ---
[INFO] --- archetype:3.4.1:jar (default-jar) @ greeter-archetype ---
[INFO] --- archetype:3.4.1:integration-test (default-integration-test) @ greeter-archetype ---
[INFO] [INFO] Running org.acme.basic.AppTest
[INFO] [INFO] BUILD SUCCESS
[INFO] Post-archetype-generation invoker exit code: 0
[INFO] --- install:3.1.4:install (default-install) @ greeter-archetype ---
[INFO] --- archetype:3.4.1:update-local-catalog (default-update-local-catalog) @ greeter-archetype ---
[INFO] Updated local archetypes catalog /home/user/.m2/repository/archetype-catalog.xml
[INFO] BUILD SUCCESS
...
$ cat ~/.m2/repository/archetype-catalog.xml
<?xml version="1.0" encoding="UTF-8"?>
<archetype-catalog xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0 https://maven.apache.org/xsd/archetype-catalog-1.0.0.xsd"
xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<archetypes>
<archetype>
<groupId>com.example</groupId>
<artifactId>greeter-archetype</artifactId>
<version>1.0.0</version>
<description>Creates a Java 25 console application with JUnit 6</description>
</archetype>
</archetypes>
</archetype-catalog>
Using the archetype
We generate a project from the installed archetype. The
greeting property is set on the command line; the
javaVersion property takes its default value.
The Slovak greeting Ahoj was written into the source code, and the
project name hello into the fallback name. To share the archetype
with others, deploy it to a shared repository with mvn deploy;
users then need only its coordinates.
$ mvn archetype:generate -DarchetypeGroupId=com.example \
-DarchetypeArtifactId=greeter-archetype -DarchetypeVersion=1.0.0 \
-DgroupId=org.acme -DartifactId=hello -Dgreeting=Ahoj \
-DinteractiveMode=false
[INFO] Parameter: groupId, Value: org.acme
[INFO] Parameter: artifactId, Value: hello
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: org.acme
[INFO] Parameter: packageInPathFormat, Value: org/acme
[INFO] Parameter: javaVersion, Value: 25
[INFO] Parameter: greeting, Value: Ahoj
[INFO] Project created from Archetype in dir: /home/user/hello
[INFO] BUILD SUCCESS
...
$ cd hello
$ cat src/main/java/org/acme/App.java
package org.acme;
public class App {
static String greet(String name) {
return "Ahoj, " + name + "!";
}
public static void main(String[] args) {
String name = args.length > 0 ? args[0] : "hello";
System.out.println(greet(name));
}
}
$ mvn -q package
$ java -cp target/hello-1.0-SNAPSHOT.jar org.acme.App Maven
Ahoj, Maven!
Interactive mode
In interactive mode, the plugin asks for each property that has no value;
a value in the prompt is the default, taken when we press Enter.
Then it asks for a confirmation. If we answer N, it asks for all
the values again; the archetype properties keep their current values as
defaults, but the groupId and artifactId must be
typed again. Here we replace the greeting and try an invalid Java version,
which the validation regex rejects.
$ mvn archetype:generate -DarchetypeGroupId=com.example \ -DarchetypeArtifactId=greeter-archetype -DarchetypeVersion=1.0.0 [INFO] Using property: greeting = Hello [INFO] Using property: javaVersion = 25 Define value for property 'groupId': org.acme Define value for property 'artifactId': hi Define value for property 'version' 1.0-SNAPSHOT: Define value for property 'package' org.acme: Confirm properties configuration: greeting: Hello javaVersion: 25 groupId: org.acme artifactId: hi version: 1.0-SNAPSHOT package: org.acme Y: N Define value for property 'greeting' Hello: Hey Define value for property 'javaVersion' (should match expression '\d+') 25: latest Value does not match the expression, please try again 25: 21 Define value for property 'groupId': org.acme Define value for property 'artifactId': hi Define value for property 'version' 1.0-SNAPSHOT: Define value for property 'package' org.acme: Confirm properties configuration: greeting: Hey javaVersion: 21 groupId: org.acme artifactId: hi version: 1.0-SNAPSHOT package: org.acme Y: Y [INFO] Project created from Archetype in dir: /home/user/hi
Creating an archetype from a project
Instead of writing an archetype by hand, we can derive it from an existing
project with the archetype:create-from-project goal. Our
starter project is an ordinary project with an application class
and a test.
The goal writes a complete archetype project into
target/generated-sources/archetype and then builds it. In version
3.4.1 of the plugin, the build step fails with The specified user
settings file does not exist when there is no
~/.m2/settings.xml file. Passing any settings file with
-s avoids the problem; we use one that contains only
<settings/>.
The plugin replaced the com.example.starter package with
${package} and the starter name with
${artifactId}, and added Velocity definitions that protect
$ and # characters. The generated archetype is a
starting point: we review the templates, add required properties, and then
install it from the target/generated-sources/archetype
directory with mvn install.
$ mvn archetype:create-from-project -s ../settings.xml
[INFO] Building starter 1.0.0
[INFO] --- archetype:3.4.1:create-from-project (default-cli) @ starter ---
[INFO] Building starter-archetype 1.0.0
[INFO] --- resources:3.3.1:resources (default-resources) @ starter-archetype ---
[INFO] --- resources:3.3.1:testResources (default-testResources) @ starter-archetype ---
[INFO] --- archetype:3.4.1:jar (default-jar) @ starter-archetype ---
[INFO] Building archetype jar: /home/user/starter/target/generated-sources/archetype/target/starter-archetype-1.0.0.jar
[INFO] Building jar: /home/user/starter/target/generated-sources/archetype/target/starter-archetype-1.0.0.jar
[INFO] BUILD SUCCESS
[INFO] BUILD SUCCESS
...
$ cd target/generated-sources/archetype
$ find . -type f | sort
./pom.xml
./src/main/resources/META-INF/maven/archetype-metadata.xml
./src/main/resources/archetype-resources/pom.xml
./src/main/resources/archetype-resources/src/main/java/App.java
./src/main/resources/archetype-resources/src/test/java/AppTest.java
./src/test/resources/projects/basic/archetype.properties
./src/test/resources/projects/basic/goal.txt
./target/classes/META-INF/maven/archetype-metadata.xml
./target/classes/archetype-resources/pom.xml
./target/classes/archetype-resources/src/main/java/App.java
./target/classes/archetype-resources/src/test/java/AppTest.java
./target/maven-archiver/pom.properties
./target/starter-archetype-1.0.0.jar
./target/test-classes/projects/basic/archetype.properties
./target/test-classes/projects/basic/goal.txt
$ head -4 src/main/resources/archetype-resources/src/main/java/App.java
#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
package ${package};
Source
In this article we have generated projects from Maven archetypes and created our own archetype.
Author
List all Java tutorials.