diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index aa2bd188d81c0f..b6dbc7b09d1d24 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -2817,6 +2817,21 @@
quarkus-container-image-util
${project.version}
+
+ io.quarkus
+ quarkus-dockerfiles
+ ${project.version}
+
+
+ io.quarkus
+ quarkus-dockerfiles-spi
+ ${project.version}
+
+
+ io.quarkus
+ quarkus-dockerfiles-deployment
+ ${project.version}
+
io.quarkus
quarkus-kubernetes
@@ -6847,7 +6862,6 @@
${project.version}
-
diff --git a/extensions/dockerfiles/cli/pom.xml b/extensions/dockerfiles/cli/pom.xml
new file mode 100644
index 00000000000000..0d4e6e83938d94
--- /dev/null
+++ b/extensions/dockerfiles/cli/pom.xml
@@ -0,0 +1,106 @@
+
+
+ quarkus-dockerfiles-parent
+ io.quarkus
+ 999-SNAPSHOT
+ ../pom.xml
+
+ 4.0.0
+
+ quarkus-dockerfiles-cli
+ Quarkus - Dockerfiles - CLI
+ CLI plugin that provides commands for Dockerfile genration
+
+
+ uber-jar
+
+
+
+
+ io.quarkus
+ quarkus-picocli
+
+
+
+ io.quarkus
+ quarkus-arc
+
+
+
+ io.quarkus
+ quarkus-devtools-common
+
+
+
+ io.quarkus
+ quarkus-bootstrap-maven-resolver
+
+
+
+ io.quarkus
+ quarkus-dockerfiles-spi
+
+
+
+
+ io.quarkus
+ quarkus-picocli-deployment
+ pom
+ test
+ ${project.version}
+
+
+ *
+ *
+
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+
+
+
+ build
+ generate-code
+ generate-code-tests
+
+
+ true
+
+ ${settings.localRepository}
+ ${env.MAVEN_OPTS}
+
+
+
+
+
+
+ maven-compiler-plugin
+
+
+ -parameters
+
+
+
+
+
+
diff --git a/extensions/dockerfiles/cli/src/main/java/io/quarkus/dockerfiles/cli/Dockerfiles.java b/extensions/dockerfiles/cli/src/main/java/io/quarkus/dockerfiles/cli/Dockerfiles.java
new file mode 100644
index 00000000000000..6da273a19e0473
--- /dev/null
+++ b/extensions/dockerfiles/cli/src/main/java/io/quarkus/dockerfiles/cli/Dockerfiles.java
@@ -0,0 +1,134 @@
+package io.quarkus.dockerfiles.cli;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+import java.util.function.Consumer;
+
+import io.quarkus.bootstrap.BootstrapException;
+import io.quarkus.bootstrap.app.AugmentAction;
+import io.quarkus.bootstrap.app.CuratedApplication;
+import io.quarkus.bootstrap.app.QuarkusBootstrap;
+import io.quarkus.devtools.project.BuildTool;
+import io.quarkus.devtools.project.QuarkusProjectHelper;
+import io.quarkus.dockerfiles.spi.GeneratedDockerfile;
+import io.quarkus.picocli.runtime.annotations.TopCommand;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.ExitCode;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
+
+@TopCommand
+@Command(name = "dockerfiles", sortOptions = false, mixinStandardHelpOptions = false, header = "Generate Dockerfiles.", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", optionListHeading = "%nOptions:%n")
+public class Dockerfiles implements Callable {
+
+ @Option(names = { "--jvm" }, paramLabel = "", order = 5, description = "Flag to enable JVM Dockerfile generation")
+ boolean generateJvmDockerfile;
+
+ @Option(names = { "--native" }, paramLabel = "", order = 5, description = "Flag to enable Native Dockerfile generation")
+ boolean generateNativeDockerfile;
+
+ @Parameters(arity = "0..1", paramLabel = "GENERATION_PATH", description = " The path to generate Dockerfiles")
+ Optional generationPath;
+
+ public Integer call() {
+ Path projectRoot = getWorkingDirectory();
+ BuildTool buildTool = QuarkusProjectHelper.detectExistingBuildTool(projectRoot);
+ if (buildTool == null) {
+ System.out.println("Unable to determine the build tool used for the project at " + projectRoot);
+ return ExitCode.USAGE;
+ }
+ Path targetDirecotry = projectRoot.resolve(buildTool.getBuildDirectory());
+ QuarkusBootstrap quarkusBootstrap = QuarkusBootstrap.builder()
+ .setMode(QuarkusBootstrap.Mode.PROD)
+ .setApplicationRoot(getWorkingDirectory())
+ .setProjectRoot(getWorkingDirectory())
+ .setTargetDirectory(targetDirecotry)
+ .setLocalProjectDiscovery(true)
+ .setIsolateDeployment(false)
+ .setBaseClassLoader(ClassLoader.getSystemClassLoader())
+ .build();
+
+ List resultBuildItemFQCNs = new ArrayList<>();
+
+ boolean hasJvmSuffix = generationPath.map(p -> p.endsWith(".jvm")).orElse(false);
+ boolean hasNativeSuffix = generationPath.map(p -> p.endsWith(".native")).orElse(false);
+ boolean isDirectory = generationPath.map(p -> Paths.get(p).toFile().isDirectory())
+ .orElse(Paths.get("").toFile().isDirectory());
+
+ // Checking
+ if (generateJvmDockerfile && hasNativeSuffix) {
+ System.out.println("Cannot generate JVM Dockerfile when the path has a .native suffix");
+ return ExitCode.USAGE;
+ }
+ if (generateNativeDockerfile && hasJvmSuffix) {
+ System.out.println("Cannot generate Native Dockerfile when the path has a .jvm suffix");
+ return ExitCode.USAGE;
+ } else if (generateJvmDockerfile && generateNativeDockerfile && !isDirectory) {
+
+ }
+
+ if (generateJvmDockerfile || hasJvmSuffix) {
+ resultBuildItemFQCNs.add(GeneratedDockerfile.Jvm.class.getName());
+ }
+
+ if (generateNativeDockerfile || hasNativeSuffix) {
+ resultBuildItemFQCNs.add(GeneratedDockerfile.Native.class.getName());
+ }
+
+ if (resultBuildItemFQCNs.isEmpty()) {
+ generateJvmDockerfile = true;
+ resultBuildItemFQCNs.add(GeneratedDockerfile.Jvm.class.getName());
+ }
+
+ Path jvmDockerfile = (isDirectory
+ ? generationPath.map(p -> Paths.get(p))
+ : generationPath.map(Paths::get))
+ .orElse(Paths.get("Dockerfile.jvm"));
+
+ Path nativeDockerfile = (isDirectory
+ ? generationPath.map(p -> Paths.get(p))
+ : generationPath.map(Paths::get))
+ .orElse(Paths.get("Dockerfile.native"));
+
+ try (CuratedApplication curatedApplication = quarkusBootstrap.bootstrap()) {
+ AugmentAction action = curatedApplication.createAugmentor();
+
+ action.performCustomBuild(GenerateDockerfilesHandler.class.getName(), new Consumer>() {
+ @Override
+ public void accept(List dockerfiles) {
+ for (GeneratedDockerfile dockerfile : dockerfiles) {
+ if (dockerfile instanceof GeneratedDockerfile.Jvm) {
+ writeStringSafe(jvmDockerfile, dockerfile.getContent());
+ System.out.println("Generated JVM Dockerfile: " + jvmDockerfile);
+ } else if (dockerfile instanceof GeneratedDockerfile.Native) {
+ writeStringSafe(nativeDockerfile, dockerfile.getContent());
+ System.out.println("Generated Native Dockerfile: " + nativeDockerfile);
+ }
+ }
+ }
+ }, resultBuildItemFQCNs.toArray(new String[resultBuildItemFQCNs.size()]));
+
+ } catch (BootstrapException e) {
+ throw new RuntimeException(e);
+ }
+ return ExitCode.OK;
+ }
+
+ private void writeStringSafe(Path p, String content) {
+ try {
+ Files.writeString(p, content);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Path getWorkingDirectory() {
+ return Paths.get(System.getProperty("user.dir"));
+ }
+}
diff --git a/extensions/dockerfiles/cli/src/main/java/io/quarkus/dockerfiles/cli/GenerateDockerfilesHandler.java b/extensions/dockerfiles/cli/src/main/java/io/quarkus/dockerfiles/cli/GenerateDockerfilesHandler.java
new file mode 100644
index 00000000000000..3283b3fd818c39
--- /dev/null
+++ b/extensions/dockerfiles/cli/src/main/java/io/quarkus/dockerfiles/cli/GenerateDockerfilesHandler.java
@@ -0,0 +1,32 @@
+package io.quarkus.dockerfiles.cli;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+import io.quarkus.builder.BuildResult;
+import io.quarkus.dockerfiles.spi.GeneratedDockerfile;
+import io.quarkus.dockerfiles.spi.GeneratedDockerfile.Jvm;
+import io.quarkus.dockerfiles.spi.GeneratedDockerfile.Native;
+
+public class GenerateDockerfilesHandler implements BiConsumer