Structuring Multi-Project Builds
As your codebase grows, organizing it into multiple subprojects becomes essential for maintainability, performance, and reuse. Gradle supports this with multi-project builds.

While some small projects and monolithic applications may contain a single build file and source tree, it is often more common for a project to have been split into smaller, interdependent modules. The word "interdependent" is vital, as you typically want to link the many modules together through a single build.
Gradle supports this scenario through multi-project builds. This is sometimes referred to as a multi-module project. Gradle refers to modules as subprojects.
A multi-project build consists of one root project and one or more subprojects.
Multi-Project Structure
The following represents the structure of a sample multi-project build that contains three subprojects:

The directory structure should look as follows:
.
├── gradlew
├── gradlew.bat
├── settings.gradle(.kts) (1)
├── sub-project-1
│ └── build.gradle(.kts) (2)
├── sub-project-2
│ └── build.gradle(.kts) (2)
└── sub-project-3
└── build.gradle(.kts) (2)
1 | The settings.gradle(.kts) file should include all subprojects. |
2 | Each subproject should have its own build.gradle(.kts) file. |
In this example, the root settings file will look as follows:
include("sub-project-1", "sub-project-2", "sub-project-3")
include('sub-project-1', 'sub-project-2', 'sub-project-3')
The order in which the subprojects (modules) are included does not matter. |
The Gradle community has two standards for multi-project build structures:
-
Multi-Project Builds using
buildSrc
- wherebuildSrc
is a subproject-like directory at the Gradle project root containing shared build logic. -
Composite Builds including
build-logic
- a build that includes other builds wherebuild-logic
is a build directory at the Gradle project root containing reusable build logic.

In either case, the build-logic
and buildSrc
folders are used to organize build logic.
Each approach has trade-offs.
buildSrc
is easier to get started with but less flexible.
Composite builds require a bit more setup but scale better and align with Gradle’s long-term best practices for sharing build logic.
Multi-Project Paths
A project path has the following pattern: it starts with an optional colon, which denotes the root project.
The root project, :
, is the only project in a path not specified by its name.
The rest of a project path is a colon-separated sequence of project names, where the next project is a subproject of the previous project:
:sub-project-1
You can see the project paths when running gradle projects
:
------------------------------------------------------------
Root project 'project'
------------------------------------------------------------
Root project 'project'
+--- Project ':sub-project-1'
\--- Project ':sub-project-2'
Project paths usually reflect the filesystem layout, but there are exceptions. Most notably for composite builds.
Executing tasks by name
The command gradle test
will execute the test
task in any subprojects relative to the current working directory that has that task.
If you run the command from the root project directory, you will run test
in sub-project-1
, sub-project-2
, and sub-project-3
.
The basic rule behind Gradle’s behavior is to execute all tasks down the hierarchy with this name. And complain if there is no such task found in any of the subprojects traversed.
Some task selectors, like help or dependencies , will only run the task on the project they are invoked on and not on all the subprojects to reduce the amount of information printed on the screen.
|
Executing tasks by fully qualified name
In a multi-project build, you can run tasks for a specific subproject by using the task’s fully qualified name. This name combines the project path and the task name.
For example, to run the build task in sub-project-1
, use ./gradlew :sub-project-1:build
.
This ensures that only sub-project-1’s `build
task is executed, rather than running build across the entire build.
You can use this pattern for any task.
For example, to list all tasks available in sub-project-3
, run ./gradlew :sub-project-3:tasks
.
Multi-Project Builds using buildSrc
Multi-project builds allow you to organize projects with many modules, wire dependencies between those modules, and easily share common build logic amongst them.
For example, if the project above had common build logic between sub-project-1
, sub-project-2
and sub-project-3
, it could be structured as follows:
.
├── gradlew
├── gradlew.bat
├── settings.gradle(.kts)
├── buildSrc (1)
│ ├── build.gradle.kts
│ └── src/main/*/shared-build-conventions.gradle(.kts) (2)
├── sub-project-1
│ └── build.gradle(.kts) (3)
├── sub-project-2
│ └── build.gradle(.kts) (3)
└── sub-project-3
└── build.gradle(.kts) (3)
1 | Gradle recognized buildSrc folder |
2 | Contains common build logic from sub-project-1 , sub-project-2 and sub-project-3 |
3 | Applies shared-build-conventions.gradle(.kts) |
The buildSrc
directory is automatically recognized by Gradle.
It is a good place to define and maintain shared configuration or imperative build logic, such as custom tasks or plugins.
buildSrc
is automatically included in your build as a special subproject if a build.gradle(.kts)
file is found under buildSrc
.
Consult the Sharing Build Logic using buildSrc
chapter to learn more.
Composite Builds including build-logic
Composite Builds, also referred to as included builds, are best for sharing logic between builds (not subprojects) or isolating access to shared build logic.
Let’s take the previous example.
The logic in buildSrc
has been turned into a project that contains plugins and can be published and worked on independently of the root project build.
The plugin is moved to its own build called build-logic
with its own build script and settings file:
.
├── gradlew
├── gradlew.bat
├── settings.gradle(.kts)
├── build-logic (1)
│ ├── settings.gradle.kts
│ └── conventions
│ ├── build.gradle.kts
│ └── src/main/kotlin/shared-build-conventions.gradle.kts (2)
├── sub-project-1
│ └── build.gradle(.kts) (3)
├── sub-project-2
│ └── build.gradle(.kts) (3)
└── sub-project-3
└── build.gradle(.kts) (3)
1 | Separate Gradle build called build-logic |
2 | Contains common build logic from sub-project-1 , sub-project-2 and sub-project-3 |
3 | Applies shared-build-conventions.gradle(.kts) |
The fact that build-logic is located in a subdirectory of the root project is irrelevant. The folder could be located outside the root project if desired.
|
The root settings file includes the entire build-logic
build:
include("sub-project-1", "sub-project-2", "sub-project-3")
includeBuild("build-logic")
include('sub-project-1', 'sub-project-2', 'sub-project-3')
includeBuild('build-logic')
There’s no reason that any of the subprojects in a multi-project build couldn’t themselves be composite builds. This allows teams to independently develop and test build logic or components, then include them in a larger build as needed. For example:
.
├── gradlew
├── gradlew.bat
├── settings.gradle(.kts)
├── build-logic (1)
│ ├── settings.gradle(.kts)
│ └── conventions
│ └── build.gradle(.kts)
├── project-1 (2)
│ ├── settings.gradle(.kts)
│ ├── client
│ │ └── build.gradle(.kts)
│ └── server
│ └── build.gradle(.kts)
├── project-2 (3)
│ ├── settings.gradle(.kts)
│ └── lib
│ └── build.gradle(.kts)
└── project-3 (4)
├── settings.gradle(.kts)
├── app-plugin
│ └── build.gradle(.kts)
├── client-plugin
│ └── build.gradle(.kts)
└── server-plugin
└── build.gradle(.kts)
1 | Separate Gradle build called build-logic |
2 | Separate Gradle build called project-1 with 2 of its own subproject |
3 | Separate Gradle build called project-2 with 1 of its own subproject |
4 | Separate Gradle build called project-3 with 3 of its own subprojects |
In this setup, a team could work on project-3
as an entirely independent build.
Once their changes are complete, another team could test and validate those changes by integrating `project-3’s changes into the full root build.
Consult the Composite Builds chapter to learn more.
Next Step: Learn about the Gradle Build Lifecycle >>