diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index f4c55ad27a227451a862daf1e34fff766bf3f39d..a31af6758fb1658493c23da4c3ede1014d074061 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -443,6 +443,7 @@ For `sbt` note that Renovate will update the version string only for packages th
 ## cloneSubmodules
 
 Enabling this option will mean that any detected Git submodules will be cloned at time of repository clone.
+Submodules are always cloned recursively.
 
 Important: private submodules aren't supported by Renovate, unless the underlying `ssh` layer already has the correct permissions.
 
diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts
index b29115cab23235428e47c7b9d7771e0e82253bb0..556390567311dcef83fd0a7a399bd59eb931e97b 100644
--- a/lib/util/git/index.ts
+++ b/lib/util/git/index.ts
@@ -354,7 +354,9 @@ export async function cloneSubmodules(shouldClone: boolean): Promise<void> {
   for (const submodule of submodules) {
     try {
       logger.debug(`Cloning git submodule at ${submodule}`);
-      await gitRetry(() => git.submoduleUpdate(['--init', submodule]));
+      await gitRetry(() =>
+        git.submoduleUpdate(['--init', '--recursive', submodule]),
+      );
     } catch (err) {
       logger.warn(
         { err },