[{"data":1,"prerenderedAt":723},["ShallowReactive",2],{"/en-us/blog/use-gitlab-duo-to-build-and-deploy-a-simple-quarkus-native-project/":3,"navigation-en-us":41,"banner-en-us":468,"footer-en-us":485,"Cesar Saavedra":695,"next-steps-en-us":708},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":30,"_id":34,"_type":35,"title":36,"_source":37,"_file":38,"_stem":39,"_extension":40},"/en-us/blog/use-gitlab-duo-to-build-and-deploy-a-simple-quarkus-native-project","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"Use GitLab Duo to build and deploy a simple Quarkus-native project","This tutorial shows how a Java application is compiled to machine code and deployed to a Kubernetes cluster using a CI/CD pipeline. See how AI makes the process faster and more efficient.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749666069/Blog/Hero%20Images/AdobeStock_639935439.jpg","https://about.gitlab.com/blog/use-gitlab-duo-to-build-and-deploy-a-simple-quarkus-native-project","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Use GitLab Duo to build and deploy a simple Quarkus-native project\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Cesar Saavedra\"}],\n        \"datePublished\": \"2024-10-17\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":19,"body":20,"category":21,"tags":22},[18],"Cesar Saavedra","2024-10-17","In [“How to automate software delivery using Quarkus and\nGitLab,”](https://about.gitlab.com/blog/how-to-automate-software-delivery-using-quarkus-and-gitlab/)\nyou learned how to develop and deploy a simple Quarkus-JVM application to a\nKubernetes cluster using [GitLab Auto\nDevOps](https://docs.gitlab.com/ee/topics/autodevops/). Now, you'll learn\nhow to use Quarkus-native to compile a Java application to machine code and\ndeploy it to a Kubernetes cluster using a CI/CD pipeline. Follow our journey\nfrom development to deployment leveraging [GitLab\nDuo](https://about.gitlab.com/gitlab-duo/) as our AI companion, including\nthe specific prompts we used.\n\n\n## What is Quarkus?\n\n\n[Quarkus](https://quarkus.io/), also known as the Supersonic Subatomic Java,\nis an open source, Kubernetes-native Java stack tailored to OpenJDK HotSpot\nand GraalVM. The Quarkus project recently moved to the [Commonhaus\nFoundation](https://www.commonhaus.org/), a nonprofit organization dedicated\nto the sustainability of open source libraries and frameworks that provides\na balanced approach to governance and support.\n\n\n## Prerequisites\n\n\nThis tutorial assumes:\n\n\n- You have a running Kubernetes cluster, e.g. GKE.\n\n- You have access to the Kubernetes cluster from your local laptop via the\n`kubectl` command.\n\n- The cluster is connected to your GitLab project.\n\n- You have [Maven (Version 3.9.6 or later)](https://maven.apache.org/)\ninstalled on your local laptop.\n\n- You have Visual Studio Code installed on your local laptop.\n\n\nIf you’d like to set up a Kubernetes cluster connected to your GitLab\nproject, you can follow the instructions in this\n[tutorial](https://about.gitlab.com/blog/eliminate-risk-with-feature-flags-tutorial/),\nup to but not including the “Creating an instance of MySQL database in your\ncluster via Flux” section (you do not need a database for this tutorial).\n\n\nYou will also need to install an nginx ingress in your Kubernetes cluster.\nHere are two ways to do this:\n\n1. You can follow the instructions in [“Creating and importing\nprojects”](https://about.gitlab.com/blog/eliminate-risk-with-feature-flags-tutorial/#creating-and-importing-projects),\nup to the creation of the variable `KUBE_INGRESS_BASE_DOMAIN`.\n\n2. Or, just create an ingress in your Kubernetes cluster by following the\ninstructions in our [Auto DevOps with GKE\ndocumentation](https://docs.gitlab.com/ee/topics/autodevops/cloud_deployments/auto_devops_with_gke.html#install-ingress).\n\n\n**NOTE:** For this article, we used the first method above to install an\ningress and cert-manager in the Kubernetes cluster.\n\n\n## Creating necessary project files using GitLab Duo Chat\n\n\nWe started our endeavor from VS Code and an empty project called\n`quarkus-native`, which we had previously created in GitLab and had already\ncloned to our local laptop.\n\n\n1. We opened GitLab Duo Chat, within VS Code, and entered the following\nprompt:\n\n\n**_Create a “Hello World” Quarkus application that can be natively\ncompiled_**\n\n\n![hello-world-prompt](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/hello-world-prompt.png)\n\n\nChat replied with the prerequisites and process of what to do to create a\nsimple “Hello World” Quarkus application. At this point, we were after the\ngenerated body of the class `HelloResource.java`:\n\n\n```\n\npackage org.acme;\n\n\nimport javax.ws.rs.GET;\n\nimport javax.ws.rs.Path;\n\nimport javax.ws.rs.Produces;\n\nimport javax.ws.rs.core.MediaType;\n\n\n@Path(\"/hello\")\n\npublic class HelloResource {\n\n    @GET\n    @Produces(MediaType.TEXT_PLAIN)\n    public String hello() {\n        return \"Hello World\";\n    }\n}\n\n```\n\n\n2. We knew that for Quarkus we needed a special Dockerfile, named\n`Dockerfile.native`, as part of the creation of the image for the\napplication executable. We then entered the following prompt in Chat:\n\n\n**_Create the Dockerfile.native file for the sample application above_**\n\n\n![create-dockerfile-prompt](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675941/Blog/Content%20Images/create-dockerfile-prompt.png)\n\n\nChat replied with a very thorough answer about this file, its contents, and\nthe containerization process. At this point, we were just after the\ngenerated body of the file `Dockerfile.native`:\n\n\n```\n\n####\n\n# This Dockerfile is used in order to build a container that runs the\nQuarkus application in native (no JVM) mode\n\n####\n\nFROM registry.access.redhat.com/ubi8/ubi-minimal:8.6\n\nWORKDIR /work/\n\nRUN chown 1001 /work \\\n    && chmod \"g+rwX\" /work \\\n    && chown 1001:root /work\nCOPY --chown=1001:root target/*-runner /work/application\n\n\nEXPOSE 8080\n\nUSER 1001\n\n\nCMD [\"./application\", \"-Dquarkus.http.host=0.0.0.0\"]\n\n```\n\n\n3. Next, we asked Chat to generate the `pom.xml` for this application by\nentering the following prompt:\n\n\n**_Create the pom.xml file for the sample application above_**\n\n\n![create-pom-prompt](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/create-pom-prompt.png)\n\n\nChat provided the content of the `pom.xml` file, an explanation of its\ncontents, and how to use it. At this point, we were after the generated body\nof the file `pom.xml`:\n\n\n```\n\n\u003C?xml version=\"1.0\"?>\n\n\u003Cproject xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0\nhttps://maven.apache.org/xsd/maven-4.0.0.xsd\"\nxmlns=\"http://maven.apache.org/POM/4.0.0\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n  \u003CmodelVersion>4.0.0\u003C/modelVersion>\n  \u003CgroupId>org.acme\u003C/groupId>\n  \u003CartifactId>hello-world-quarkus\u003C/artifactId>\n  \u003Cversion>1.0.0-SNAPSHOT\u003C/version>\n  \u003Cproperties>\n    \u003Ccompiler-plugin.version>3.10.1\u003C/compiler-plugin.version>\n    \u003Cmaven.compiler.release>11\u003C/maven.compiler.release>\n    \u003Cproject.build.sourceEncoding>UTF-8\u003C/project.build.sourceEncoding>\n    \u003Cproject.reporting.outputEncoding>UTF-8\u003C/project.reporting.outputEncoding>\n    \u003Cquarkus.platform.artifact-id>quarkus-bom\u003C/quarkus.platform.artifact-id>\n    \u003Cquarkus.platform.group-id>io.quarkus.platform\u003C/quarkus.platform.group-id>\n    \u003Cquarkus.platform.version>2.16.5.Final\u003C/quarkus.platform.version>\n    \u003CskipITs>true\u003C/skipITs>\n    \u003Csurefire-plugin.version>3.0.0-M7\u003C/surefire-plugin.version>\n  \u003C/properties>\n  \u003CdependencyManagement>\n    \u003Cdependencies>\n      \u003Cdependency>\n        \u003CgroupId>${quarkus.platform.group-id}\u003C/groupId>\n        \u003CartifactId>${quarkus.platform.artifact-id}\u003C/artifactId>\n        \u003Cversion>${quarkus.platform.version}\u003C/version>\n        \u003Ctype>pom\u003C/type>\n        \u003Cscope>import\u003C/scope>\n      \u003C/dependency>\n    \u003C/dependencies>\n  \u003C/dependencyManagement>\n  \u003Cdependencies>\n    \u003Cdependency>\n      \u003CgroupId>io.quarkus\u003C/groupId>\n      \u003CartifactId>quarkus-arc\u003C/artifactId>\n    \u003C/dependency>\n    \u003Cdependency>\n      \u003CgroupId>io.quarkus\u003C/groupId>\n      \u003CartifactId>quarkus-resteasy\u003C/artifactId>\n    \u003C/dependency>\n    \u003Cdependency>\n      \u003CgroupId>io.quarkus\u003C/groupId>\n      \u003CartifactId>quarkus-junit5\u003C/artifactId>\n      \u003Cscope>test\u003C/scope>\n    \u003C/dependency>\n    \u003Cdependency>\n      \u003CgroupId>io.rest-assured\u003C/groupId>\n      \u003CartifactId>rest-assured\u003C/artifactId>\n      \u003Cscope>test\u003C/scope>\n    \u003C/dependency>\n  \u003C/dependencies>\n  \u003Cbuild>\n    \u003Cplugins>\n      \u003Cplugin>\n        \u003CgroupId>${quarkus.platform.group-id}\u003C/groupId>\n        \u003CartifactId>quarkus-maven-plugin\u003C/artifactId>\n        \u003Cversion>${quarkus.platform.version}\u003C/version>\n        \u003Cextensions>true\u003C/extensions>\n        \u003Cexecutions>\n          \u003Cexecution>\n            \u003Cgoals>\n              \u003Cgoal>build\u003C/goal>\n              \u003Cgoal>generate-code\u003C/goal>\n              \u003Cgoal>generate-code-tests\u003C/goal>\n            \u003C/goals>\n          \u003C/execution>\n        \u003C/executions>\n      \u003C/plugin>\n      \u003Cplugin>\n        \u003CartifactId>maven-compiler-plugin\u003C/artifactId>\n        \u003Cversion>${compiler-plugin.version}\u003C/version>\n        \u003Cconfiguration>\n          \u003CcompilerArgs>\n            \u003Carg>-parameters\u003C/arg>\n          \u003C/compilerArgs>\n        \u003C/configuration>\n      \u003C/plugin>\n      \u003Cplugin>\n        \u003CartifactId>maven-surefire-plugin\u003C/artifactId>\n        \u003Cversion>${surefire-plugin.version}\u003C/version>\n        \u003Cconfiguration>\n          \u003CsystemPropertyVariables>\n            \u003Cjava.util.logging.manager>org.jboss.logmanager.LogManager\u003C/java.util.logging.manager>\n            \u003Cmaven.home>${maven.home}\u003C/maven.home>\n          \u003C/systemPropertyVariables>\n        \u003C/configuration>\n      \u003C/plugin>\n      \u003Cplugin>\n        \u003CartifactId>maven-failsafe-plugin\u003C/artifactId>\n        \u003Cversion>${surefire-plugin.version}\u003C/version>\n        \u003Cexecutions>\n          \u003Cexecution>\n            \u003Cgoals>\n              \u003Cgoal>integration-test\u003C/goal>\n              \u003Cgoal>verify\u003C/goal>\n            \u003C/goals>\n            \u003Cconfiguration>\n              \u003CsystemPropertyVariables>\n                \u003Cnative.image.path>${project.build.directory}/${project.build.finalName}-runner\u003C/native.image.path>\n                \u003Cjava.util.logging.manager>org.jboss.logmanager.LogManager\u003C/java.util.logging.manager>\n                \u003Cmaven.home>${maven.home}\u003C/maven.home>\n              \u003C/systemPropertyVariables>\n            \u003C/configuration>\n          \u003C/execution>\n        \u003C/executions>\n      \u003C/plugin>\n    \u003C/plugins>\n  \u003C/build>\n  \u003Cprofiles>\n    \u003Cprofile>\n      \u003Cid>native\u003C/id>\n      \u003Cactivation>\n        \u003Cproperty>\n          \u003Cname>native\u003C/name>\n        \u003C/property>\n      \u003C/activation>\n      \u003Cproperties>\n        \u003CskipITs>false\u003C/skipITs>\n        \u003Cquarkus.package.type>native\u003C/quarkus.package.type>\n      \u003C/properties>\n    \u003C/profile>\n  \u003C/profiles>\n\u003C/project>\n\n```\n\n\n4. We then asked Chat to generate the Quarkus test file for this application\nby entering the following prompt:\n\n\n**_Create the content for the Quarkus test for the application above_**\n\n\n![create-quarkus-test-prompt](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/create-quarkus-test-prompt.png)\n\n\nChat returned the body for the Quarkus test, how to use it, and the body of\nthe Java class for the test needed for its native compilation. At this\npoint, we were after the generated bodies of the file\n`HelloResourceTest.java` and `HelloResourceIT.java`.\n\n\nHere is the generated body for `HelloResourceTest.java`:\n\n\n```\n\npackage org.acme;\n\n\nimport io.quarkus.test.junit.QuarkusTest;\n\nimport org.junit.jupiter.api.Test;\n\n\nimport static io.restassured.RestAssured.given;\n\nimport static org.hamcrest.CoreMatchers.is;\n\n\n@QuarkusTest\n\npublic class HelloResourceTest {\n\n    @Test\n    public void testHelloEndpoint() {\n        given()\n          .when().get(\"/hello\")\n          .then()\n             .statusCode(200)\n             .body(is(\"Hello World\"));\n    }\n\n}\n\n```\n\n\nHere is the generated body for `HelloResourceIT.java`:\n\n\n```\n\npackage org.acme;\n\n\nimport io.quarkus.test.junit.QuarkusIntegrationTest;\n\n\n@QuarkusIntegrationTest\n\npublic class HelloResourceIT extends HelloResourceTest {\n    // Execute the same tests but in native mode.\n}\n\n```\n\n\n5. We needed to know how to organize these files in the GitLab project, so\nwe asked about the directory structure for all these files by entering the\nfollowing prompt in Chat:\n\n\n**_Give me the entire directory structure for this project including the\nlocation of each file, e.g. pom.xml, Dockerfile.native,\napplication.properties, HelloResource.java, HelloResourceTest.java, and the\nlocation of the target directory_**\n\n\n![create-dir-struct-prompt](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/create-dir-struct-prompt.png)\n\n\nChat replied with a detailed diagram about the entire directory structure\nfor the project and where all these files should be located as well as a\ndescription of the purpose of each of them. It even mentioned that the\ndirectory `target/` and its contents should not be version controlled since\nit was generated by the build process. Another interesting aspect of the\nreply was the existence of a file called `resources/application.properties`\nin the directory structure.\n\n\n![dir-struct-chat-response](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/dir-struct-chat-response.png)\n\n\nWith all this information in our hands, we were ready to start creating\nthese files in our GitLab project.\n\n\n## Populating our project with the generated content for each file\n\n\nWe created each of the following files in their corresponding location and\ntheir generated content as provided by Chat:\n\n\n- `src/main/java/org/acme/HelloResource.java`\n\n- `resources/application.properties`\n\n- `src/test/java/org/acme/HelloResourceTest.java`\n\n- `src/test/java/org/acme/HelloResourceIT.java`\n\n- `pom.xml`\n\n- `Dockerfile.native`\n\n\n**NOTE:** We considered using GitLab Auto Deploy for this endeavor but later\nrealized that it would not be a supported option. We are mentioning this\nbecause in the video at the end of this tutorial, you will see that we asked\nChat: `How to set the service internalPort to 8080 for auto deploy`. Then we\ncreated a file named `.gitlab/auto-deploy-values.yaml` with the generated\ncontent from Chat. The creation of this file is not necessary for this\ntutorial.\n\n\nBefore we started tackling the pipeline to build, containerize, and deploy\nthe application to our Kubernetes cluster, we decided to generate the\nexecutable locally on our Mac and test the application locally.\n\n\n## Testing the application locally\n\n\nHere is the process we went through to test the application on our local\nmachine.\n\n\n1. To build the application on the local Mac laptop, from a Terminal window,\nwe entered the following command:\n\n\n```\n\nmvn clean package -Pnative\n\n```\n\n\n![first-build](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/first-build.png)\n\n\nThe native compilation failed with the error message:\n\n\n`Cannot find the ‘native-image’ in the GRAALVM_HOME, JAVA_HOME and System\nPATH. Install it using ‘gu install native-image’`\n\n\n2. So, we used our trusty GitLab Duo Chat again and asked it the following:\n\n\n**_The command “mvn clean package -Pnative” is failing with error\n“java.lang.RuntimeException: Cannot find the ‘native-image’ in the\nGRAALVM_HOME, JAVA_HOME and System PATH. Install it using gu install\nnative-image”. I’m using a MacOS Sonoma. How do I fix this error on my\nMac?_**\n\n\n![how-to-fix-build-failure-prompt](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/how-to-fix-build-failure-prompt.png)\n\n\nChat replied with a detailed set of steps on how to install the necessary\nsoftware and set the appropriate environment variables.\n\n\n3. We copied and pasted the following commands from the Chat window to a\nTerminal window:\n\n\n```\n\nbrew install –cask graalvm/tap/graalvm-ce-java17\n\nexport JAVA_HOME=/Library/Java/JavaVIrtualMachines/graalvm-ce-java17-22.3.1\n\nexport GRAALVM_HOME=${JAVA_HOME}\n\nexport PATH=${GRAALVM_HOME}/bin:$PATH\n\nxattr -r -d com.apple.quarantine ${GRAALVM_HOME}/../..\n\ngu install native-image\n\n```\n\n\nThe commands above installed the community edition of GraalVM Version 22.3.1\nthat supported Java 17. We noticed, during the brew install, that the\nversion of the GraalVM being installed was `java17-22.3.1`, so we had to\nupdate the pasted value for `JAVA_HOME` from `graalvm-ce-java17-22.3.0` to\n`graalvm-ce-java17-22.3.1`.\n\n\nWe also had to run the `xattr` command to get the GraalVM, which we had\ndownloaded and installed on our Mac, out of quarantine so that it could run\nlocally. Lastly, we installed the GraalVM native-image.\n\n\n4. At this point, we again, from a Terminal window, entered the following\ncommand to build the application on the local Mac laptop:\n\n\n```\n\nmvn clean package -Pnative\n\n```\n\n\nThis time the compilation was successful and an executable was generated in\nthe `target` directory.\n\n\n![successful-local-compilation](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/successful-local-compilation.png)\n\n\n5. We ran the executable by entering the following commands from a Terminal\nwindow:\n\n\n```\n\ncd target\n\n./quarkus-native-1.0.0-SNAPSHOT-runner “-Dquarkus.http.host=0.0.0.0”\n\n```\n\n\n![executable-local-run](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/executable-local-run.png)\n\n\n6. With the application running, we opened a browser window, and in the URL\nfield, we entered:\n\n\n```\n\nhttp://localhost:8080/hello\n\n```\n\n\n![app-running-locally](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/app-running-locally.png)\n\n\nThe application returned the string `Hello World`, which was displayed in\nthe browser window.\n\n\nAt this point, we committed and pushed all the changes to our GitLab project\nand started working on creating a CI/CD pipeline that would build and deploy\nthe application to a Kubernetes cluster running on the cloud.\n\n\nBut before continuing, we remembered to add, commit, and push a `.gitignore`\nfile to our project that included the path `target/`, since this was the\ndirectory where the executable would be created and we didn’t need to keep\nit - or its contents - under version control.\n\n\n## Creating the pipeline with GitLab Duo Chat\n\n\nNow that we had already successfully tested the application locally on our\nMac, we needed to create the CI/CD pipeline that would compile the\napplication, containerize it, and deploy it to our Kubernetes cluster. We\nwanted to keep the pipeline simple, brief, and have a single environment in\nwhich to deploy it. To this end, the pipeline would not tackle multiple\nenvironments or feature branches, for example.\n\n\n1. To avoid manually creating a pipeline from scratch, we decided to once\nagain leverage Chat. We entered the following prompt\n\n\n**_Create a .gitlab-ci.yml file with 3 stages: build, containerize, and\ndeploy. Each of these stages should have a single job with the same name.\nThe build job should compile the application natively using the -Pnative\nmaven option and the builder image for mandrel jdk-22 for java17 and store\nthe application executable and its Dockerfile as artifacts. The containerize\njob should use docker to build and push the image to the built-in container\nregistry. The deploy job should rollout the containerized application, named\nquarkus-app, to the production environment in the Kubernetes cluster by\ncreating a deployment resource, a service resource and an ingress rule with\nhost ${KUBE_INGRESS_BASE_DOMAIN} and /hello prefix. The deploy job should\nalso include the url for the production environment. When setting the\nKubernetes context, use ${KUBE_CONTEXT}, and for application namespace, use\n${KUBE_NAMESPACE}_**\n\n\n![create-pipeline-prompt](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/create-pipeline-prompt.png)\n\n\nThe prompt above provided some points that we thought would be useful and\nnecessary for a good outcome. The requirements listed by the prompt were:\n\n\n- It specified that the pipeline needed to have three stages: build,\ncontainerize, and deploy.\n\n- Each stage would have a single job.\n\n- The build job should natively compile (not build) the application using\nthe -Pnative option.\n\n- The build job should use the open source software `mandrel` and use the\nversion that would support jdk-22 for Java 17. Remember that these were the\nversions that we tested on our local machine.\n\n- The build job would also need to pass the resulting executable and the\nproject Dockerfile to the next job, i.e. containerize, in the pipeline.\n\n- The containerize job should build a docker image with the executable using\nthe Dockerfile, both of which were passed as artifacts.\n\n- The containerize job should push the image to the GitLab container\nregistry.\n\n- The deploy job should use the image from the GitLab container registry,\nname it `quarkus-app`, and deploy it to the Kubernetes cluster using\ndeployment, service and ingress rule resources.\n\n- The deploy job should use the environment variables\n`${KUBE_INGRESS_BASE_DOMAIN}`, `${KUBE_CONTEXT}`, and `${KUBE_NAMESPACE}`,\nwhich had been defined as group/project variables, accordingly.\n\n\nChat responded with the following `.gitlab-ci.yml` example:\n\n\n```\n\n# Define the stages for the pipeline\n\nstages:\n  - build\n  - containerize\n  - deploy\n\n# Variables section to use GitLab CI/CD variables\n\nvariables:\n  MAVEN_OPTS: \"-Dmaven.repo.local=.m2/repository\"\n  MAVEN_CLI_OPTS: \"--batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true\"\n  CONTAINER_IMAGE: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}\n\n# Cache configuration for Maven dependencies\n\ncache:\n  paths:\n    - .m2/repository\n\n# Build stage: Compile the application natively\n\nbuild:\n  stage: build\n  image:\n    name: quay.io/quarkus/ubi-quarkus-mandrel-builder-image:22.3-java17\n    entrypoint:\n      - \"\"\n  script:\n    - ./mvnw ${MAVEN_CLI_OPTS} clean package -Pnative\n  artifacts:\n    paths:\n      - target/*-runner\n      - src/main/docker/Dockerfile.native-micro\n  # This job runs only on the main branch\n  only:\n      - main\n\n# Containerize stage: Build and push Docker image\n\ncontainerize:\n  stage: containerize\n  image: docker:20.10.16\n  services:\n    - docker:20.10.16-dind\n  variables:\n    DOCKER_TLS_CERTDIR: \"/certs\"\n  before_script:\n    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY\n  script:\n    - docker build -f src/main/docker/Dockerfile.native-micro -t ${CONTAINER_IMAGE} .\n    - docker push ${CONTAINER_IMAGE}\n  # This job runs only on the main branch\n  only:\n      - main\n\n# Deploy stage: Deploy to Kubernetes\n\ndeploy:\n  stage: deploy\n  image: \n    name: bitnami/kubectl:latest\n    entrypoint: ['']\n  script:\n    - kubectl config use-context ${KUBE_CONTEXT}\n    - kubectl config set-context --current --namespace=${KUBE_NAMESPACE}\n    - |\n      cat \u003C\u003CEOF | kubectl apply -f -\n      apiVersion: apps/v1\n      kind: Deployment\n      metadata:\n        name: quarkus-app\n      spec:\n        replicas: 1\n        selector:\n          matchLabels:\n            app: quarkus-app\n        template:\n          metadata:\n            labels:\n              app: quarkus-app\n          spec:\n            containers:\n            - name: quarkus-app\n              image: ${CONTAINER_IMAGE}\n              ports:\n              - containerPort: 8080\n      ---\n      apiVersion: v1\n      kind: Service\n      metadata:\n        name: quarkus-app\n      spec:\n        selector:\n          app: quarkus-app\n        ports:\n        - port: 80\n          targetPort: 8080\n      ---\n      apiVersion: networking.k8s.io/v1\n      kind: Ingress\n      metadata:\n        name: quarkus-app\n      spec:\n        rules:\n        - host: ${KUBE_INGRESS_BASE_DOMAIN}\n          http:\n            paths:\n            - path: /hello\n              pathType: Prefix\n              backend:\n                service:\n                  name: quarkus-app\n                  port: \n                    number: 80\n      EOF\n    - echo \"Application deployed to https://${KUBE_INGRESS_BASE_DOMAIN}/hello\"\n  # This job runs only on the main branch\n  only:\n      - main\n```\n\n\n2. There were some things we needed to adjust in the sample `.gitlab-ci.yml`\nfile above before we could commit it to our `main` branch. These are the\nupdates we made to the file:\n\n\n- We deleted all occurrences of `only: -main` because we wanted to keep of\npipeline definition file simple and with no branch-related rules.\n\n- We fixed the name of the file `Dockerfile.native-micro` to\n`Dockerfile.native`.\n\n\n3. At this point, we wanted to ensure that the deployment would be to the\n`production` environment so we asked Chat the following prompt:\n\n\n**_What is the syntax to specify an environment with its url in a\npipeline?_**\n\n\n![how-to-add-env-prompt](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/how-to-add-env-prompt.png)\n\n\nThe response from Chat included an example of how to do this so we used this\ninformation to add the following environment block to our pipeline:\n\n\n```\n  environment:\n       name: production\n       url: http://${KUBE_INGRESS_BASE_DOMAIN}/hello\n```\n\n\n4. The example provided by Chat includes a URL that started with `https` and\nwe modified that to `http` since we didn’t really need a secure connection\nfor this simple application.\n\n\n5. Lastly, we noticed that in the `build` job, there was a script `mvnw`\nthat we didn’t have in our project. So, we asked Chat the following:\n\n\n**_How can I get the mvnw script for Quarkus?_**\n\n\n![how-to-add-mvnw-prompt](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/how-to-add-mvnw-prompt.png)\n\n\nChat responded with the command to execute to bootstrap and create this\nscript. We executed this command from a Terminal window:\n\n\n```\n\nmvn wrapper:wrapper\n\n```\n\n\nWe were now ready to commit all of our changes to the `main` branch and have\nthe pipeline executed. However, on our first attempt, our first pipeline\nfailed at the build job.\n\n\n## Troubleshooting using GitLab Duo Root Cause Analysis\n\n\nOur first attempt at running our brand-new pipeline failed. So, we took\nadvantage of [GitLab Duo Root Cause\nAnalysis](https://about.gitlab.com/blog/developing-gitlab-duo-blending-ai-and-root-cause-analysis-to-fix-ci-cd/),\nwhich looks at the job logs and provides a thorough natural language\nexplanation (with examples) of the root cause of the problem and, most\nimportantly, how to fix it.\n\n\n![build-job-troubleshooting](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/build-job-troubleshooting.png)\n\n\nRoot Cause Analysis recommended we look at the compatibility of the command\nthat was trying to be executed with the image of mandrel used in the build\njob. We were not using any command with the image so we concluded that it\nmust have been the predefined `entrypoint` for the image itself. We needed\nto override this so we asked Chat the following:\n\n\n**_How do I override the entrypoint of an image using gitlab keywords?_**\n\n\n![how-to-override-entrypoint-prompt](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/how-to-override-entrypoint-prompt.png)\n\n\nChat replied with some use case examples of overriding an image entry point.\nWe used that information to update the build job image definition:\n\n\n```\n\nbuild:\n    stage: build\n    image: quay.io/quarkus/ubi-quarkus-mandrel-builder-image:22.3-java17\n    entrypoint:\n        - “”\n```\n\n\nWe committed our changes to the `main` branch, which launched a new instance\nof the pipeline. This time the build job executed successfully but the\npipeline failed at the `containerize` job.\n\n\n## Running a successful pipeline\n\n\nBefore drilling down into the log of the failed `containerize` job, we\ndecided to drill into the log of the successfully completed build job first.\nEverything looked good in the log of the build job with the exception of\nthis warning message at the very end of it:\n\n\n```\n\nWARNING: src/main/docker/Dockerfile.native: no matching files. Ensure that\nthe artifact path is relative to the working directory …\n\n``` \n\n\nWe took notice of this warning and then headed to the log of the failed\n`containerize` job. In it, we saw that the `docker build` command had failed\ndue to a non-existent Dockerfile. We ran Root Cause Analysis on the job and\namong its suggested fixes was for us to verify that the project structure\nmatched the path of the specified `Dockerfile.native` file.\n\n\n![containerize-job-troubleshooting](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/containerize-job-troubleshooting.png)\n\n\nThis information confirmed our suspicion of the misplaced\n`Dockerfile.native` file. Instead of being at the directory\n`src/main/docker` as specified in the pipeline, it was located at the root\ndirectory of the project.\n\n\nSo, we went back to our project and updated every occurrence of the location\nof this file in our `.gitlab-ci.yml` file. We modified the two locations\nwhere this happened, one in the `build` job and one in the `containerize`\njob, as follows:\n\n\n```\n\nsrc/main/docker/Dockerfile.native\n\n```\n\n\nto\n\n\n```\n\nDockerfile.native\n\n```\n\n\nWe committed our updates to the `main` branch and this time our entire\npipeline executed successfully!\n\n\n![pipeline-successful-run](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/pipeline-successful-run.png)\n\n\nOur last step was to check the running application in the `production`\nenvironment in our Kubernetes cluster.\n\n\n## Accessing the deployed application running in cluster\n\n\nOnce the pipeline ran successfully to completion, we drilled in the log file\nfor the `deploy` job. Remember, this job printed the URL of the application\nat the end of its execution. We scrolled down to the bottom of the log and\nclicked on the `https` application link, which opened a browser window\nwarning us that the connection was not private (we disabled `https` for the\nenvironment URL but forgot it for this string). We proceeded past the\nbrowser warning and then the string \"Hello World\" was displaced in the\nbrowser window indicating that the application was up and running in the\nKubernetes cluster.\n\n\nFinally, to double-check our production deployment URL, we headed to the\nproject **Operate > Environments** window, and clicked on the \"Open\" button\nfor it, which immediately opened a browser window with the \"Hello World\"\nmessage.\n\n\n![app-running-on-k8s](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749675940/Blog/Content%20Images/app-running-on-k8s.png)\n\n\n## Try it \n\n\nWe created, compiled, built, and deployed a simple Quarkus application to a\nKubernetes cluster using [GitLab Duo](https://about.gitlab.com/gitlab-duo/).\nThis approach allowed us to be more efficient and productive in all the\ntasks that we performed and it helped us streamline our DevSecOps processes.\nWe have shown only a small portion of how GitLab Duo's AI-powered\ncapabilities can help you, namely Chat and Root Cause Analysis. There’s so\nmuch more you can leverage in GitLab Duo to help you create better software\nfaster and more securely.\n\n\nWatch this whole use case in action:\n\n\n\u003C!-- blank line -->\n\n\u003Cfigure class=\"video_container\">\n  \u003Ciframe src=\"https://www.youtube.com/embed/xDpycxz3RPY?si=HHZrFt1O_8XoLATf\" frameborder=\"0\" allowfullscreen=\"true\"> \u003C/iframe>\n\u003C/figure>\n\n\u003C!-- blank line -->\n\n\nAll the project assets we used are available\n[here](https://gitlab.com/gitlab-da/use-cases/ai/ai-applications/quarkusn/quarkus-native).\n\n\n> [Try GitLab Duo for free](https://about.gitlab.com/solutions/gitlab-duo-pro/sales/?type=free-trial&toggle=gitlab-duo-pro)\nand get started on exciting projects like this.\n","ai-ml",[23,24,25,26,27,28,29],"AI/ML","tutorial","DevSecOps","demo","features","product","CI/CD",{"slug":31,"featured":32,"template":33},"use-gitlab-duo-to-build-and-deploy-a-simple-quarkus-native-project",true,"BlogPost","content:en-us:blog:use-gitlab-duo-to-build-and-deploy-a-simple-quarkus-native-project.yml","yaml","Use Gitlab Duo To Build And Deploy A Simple Quarkus Native Project","content","en-us/blog/use-gitlab-duo-to-build-and-deploy-a-simple-quarkus-native-project.yml","en-us/blog/use-gitlab-duo-to-build-and-deploy-a-simple-quarkus-native-project","yml",{"_path":42,"_dir":43,"_draft":6,"_partial":6,"_locale":7,"data":44,"_id":464,"_type":35,"title":465,"_source":37,"_file":466,"_stem":467,"_extension":40},"/shared/en-us/main-navigation","en-us",{"logo":45,"freeTrial":50,"sales":55,"login":60,"items":65,"search":395,"minimal":426,"duo":445,"pricingDeployment":454},{"config":46},{"href":47,"dataGaName":48,"dataGaLocation":49},"/","gitlab logo","header",{"text":51,"config":52},"Get free trial",{"href":53,"dataGaName":54,"dataGaLocation":49},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":56,"config":57},"Talk to sales",{"href":58,"dataGaName":59,"dataGaLocation":49},"/sales/","sales",{"text":61,"config":62},"Sign in",{"href":63,"dataGaName":64,"dataGaLocation":49},"https://gitlab.com/users/sign_in/","sign in",[66,110,206,211,316,376],{"text":67,"config":68,"cards":70,"footer":93},"Platform",{"dataNavLevelOne":69},"platform",[71,77,85],{"title":67,"description":72,"link":73},"The most comprehensive AI-powered DevSecOps Platform",{"text":74,"config":75},"Explore our Platform",{"href":76,"dataGaName":69,"dataGaLocation":49},"/platform/",{"title":78,"description":79,"link":80},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":81,"config":82},"Meet GitLab Duo",{"href":83,"dataGaName":84,"dataGaLocation":49},"/gitlab-duo/","gitlab duo ai",{"title":86,"description":87,"link":88},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":89,"config":90},"Learn more",{"href":91,"dataGaName":92,"dataGaLocation":49},"/why-gitlab/","why gitlab",{"title":94,"items":95},"Get started with",[96,101,106],{"text":97,"config":98},"Platform Engineering",{"href":99,"dataGaName":100,"dataGaLocation":49},"/solutions/platform-engineering/","platform engineering",{"text":102,"config":103},"Developer Experience",{"href":104,"dataGaName":105,"dataGaLocation":49},"/developer-experience/","Developer experience",{"text":107,"config":108},"MLOps",{"href":109,"dataGaName":107,"dataGaLocation":49},"/topics/devops/the-role-of-ai-in-devops/",{"text":111,"left":32,"config":112,"link":114,"lists":118,"footer":188},"Product",{"dataNavLevelOne":113},"solutions",{"text":115,"config":116},"View all Solutions",{"href":117,"dataGaName":113,"dataGaLocation":49},"/solutions/",[119,143,167],{"title":120,"description":121,"link":122,"items":127},"Automation","CI/CD and automation to accelerate deployment",{"config":123},{"icon":124,"href":125,"dataGaName":126,"dataGaLocation":49},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[128,131,135,139],{"text":29,"config":129},{"href":130,"dataGaLocation":49,"dataGaName":29},"/solutions/continuous-integration/",{"text":132,"config":133},"AI-Assisted Development",{"href":83,"dataGaLocation":49,"dataGaName":134},"AI assisted development",{"text":136,"config":137},"Source Code Management",{"href":138,"dataGaLocation":49,"dataGaName":136},"/solutions/source-code-management/",{"text":140,"config":141},"Automated Software Delivery",{"href":125,"dataGaLocation":49,"dataGaName":142},"Automated software delivery",{"title":144,"description":145,"link":146,"items":151},"Security","Deliver code faster without compromising security",{"config":147},{"href":148,"dataGaName":149,"dataGaLocation":49,"icon":150},"/solutions/security-compliance/","security and compliance","ShieldCheckLight",[152,157,162],{"text":153,"config":154},"Application Security Testing",{"href":155,"dataGaName":156,"dataGaLocation":49},"/solutions/application-security-testing/","Application security testing",{"text":158,"config":159},"Software Supply Chain Security",{"href":160,"dataGaLocation":49,"dataGaName":161},"/solutions/supply-chain/","Software supply chain security",{"text":163,"config":164},"Software Compliance",{"href":165,"dataGaName":166,"dataGaLocation":49},"/solutions/software-compliance/","software compliance",{"title":168,"link":169,"items":174},"Measurement",{"config":170},{"icon":171,"href":172,"dataGaName":173,"dataGaLocation":49},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[175,179,183],{"text":176,"config":177},"Visibility & Measurement",{"href":172,"dataGaLocation":49,"dataGaName":178},"Visibility and Measurement",{"text":180,"config":181},"Value Stream Management",{"href":182,"dataGaLocation":49,"dataGaName":180},"/solutions/value-stream-management/",{"text":184,"config":185},"Analytics & Insights",{"href":186,"dataGaLocation":49,"dataGaName":187},"/solutions/analytics-and-insights/","Analytics and insights",{"title":189,"items":190},"GitLab for",[191,196,201],{"text":192,"config":193},"Enterprise",{"href":194,"dataGaLocation":49,"dataGaName":195},"/enterprise/","enterprise",{"text":197,"config":198},"Small Business",{"href":199,"dataGaLocation":49,"dataGaName":200},"/small-business/","small business",{"text":202,"config":203},"Public Sector",{"href":204,"dataGaLocation":49,"dataGaName":205},"/solutions/public-sector/","public sector",{"text":207,"config":208},"Pricing",{"href":209,"dataGaName":210,"dataGaLocation":49,"dataNavLevelOne":210},"/pricing/","pricing",{"text":212,"config":213,"link":215,"lists":219,"feature":303},"Resources",{"dataNavLevelOne":214},"resources",{"text":216,"config":217},"View all resources",{"href":218,"dataGaName":214,"dataGaLocation":49},"/resources/",[220,253,275],{"title":221,"items":222},"Getting started",[223,228,233,238,243,248],{"text":224,"config":225},"Install",{"href":226,"dataGaName":227,"dataGaLocation":49},"/install/","install",{"text":229,"config":230},"Quick start guides",{"href":231,"dataGaName":232,"dataGaLocation":49},"/get-started/","quick setup checklists",{"text":234,"config":235},"Learn",{"href":236,"dataGaLocation":49,"dataGaName":237},"https://university.gitlab.com/","learn",{"text":239,"config":240},"Product documentation",{"href":241,"dataGaName":242,"dataGaLocation":49},"https://docs.gitlab.com/","product documentation",{"text":244,"config":245},"Best practice videos",{"href":246,"dataGaName":247,"dataGaLocation":49},"/getting-started-videos/","best practice videos",{"text":249,"config":250},"Integrations",{"href":251,"dataGaName":252,"dataGaLocation":49},"/integrations/","integrations",{"title":254,"items":255},"Discover",[256,261,265,270],{"text":257,"config":258},"Customer success stories",{"href":259,"dataGaName":260,"dataGaLocation":49},"/customers/","customer success stories",{"text":262,"config":263},"Blog",{"href":264,"dataGaName":5,"dataGaLocation":49},"/blog/",{"text":266,"config":267},"Remote",{"href":268,"dataGaName":269,"dataGaLocation":49},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":271,"config":272},"TeamOps",{"href":273,"dataGaName":274,"dataGaLocation":49},"/teamops/","teamops",{"title":276,"items":277},"Connect",[278,283,288,293,298],{"text":279,"config":280},"GitLab Services",{"href":281,"dataGaName":282,"dataGaLocation":49},"/services/","services",{"text":284,"config":285},"Community",{"href":286,"dataGaName":287,"dataGaLocation":49},"/community/","community",{"text":289,"config":290},"Forum",{"href":291,"dataGaName":292,"dataGaLocation":49},"https://forum.gitlab.com/","forum",{"text":294,"config":295},"Events",{"href":296,"dataGaName":297,"dataGaLocation":49},"/events/","events",{"text":299,"config":300},"Partners",{"href":301,"dataGaName":302,"dataGaLocation":49},"/partners/","partners",{"backgroundColor":304,"textColor":305,"text":306,"image":307,"link":311},"#2f2a6b","#fff","Insights for the future of software development",{"altText":308,"config":309},"the source promo card",{"src":310},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":312,"config":313},"Read the latest",{"href":314,"dataGaName":315,"dataGaLocation":49},"/the-source/","the source",{"text":317,"config":318,"lists":320},"Company",{"dataNavLevelOne":319},"company",[321],{"items":322},[323,328,334,336,341,346,351,356,361,366,371],{"text":324,"config":325},"About",{"href":326,"dataGaName":327,"dataGaLocation":49},"/company/","about",{"text":329,"config":330,"footerGa":333},"Jobs",{"href":331,"dataGaName":332,"dataGaLocation":49},"/jobs/","jobs",{"dataGaName":332},{"text":294,"config":335},{"href":296,"dataGaName":297,"dataGaLocation":49},{"text":337,"config":338},"Leadership",{"href":339,"dataGaName":340,"dataGaLocation":49},"/company/team/e-group/","leadership",{"text":342,"config":343},"Team",{"href":344,"dataGaName":345,"dataGaLocation":49},"/company/team/","team",{"text":347,"config":348},"Handbook",{"href":349,"dataGaName":350,"dataGaLocation":49},"https://handbook.gitlab.com/","handbook",{"text":352,"config":353},"Investor relations",{"href":354,"dataGaName":355,"dataGaLocation":49},"https://ir.gitlab.com/","investor relations",{"text":357,"config":358},"Trust Center",{"href":359,"dataGaName":360,"dataGaLocation":49},"/security/","trust center",{"text":362,"config":363},"AI Transparency Center",{"href":364,"dataGaName":365,"dataGaLocation":49},"/ai-transparency-center/","ai transparency center",{"text":367,"config":368},"Newsletter",{"href":369,"dataGaName":370,"dataGaLocation":49},"/company/contact/","newsletter",{"text":372,"config":373},"Press",{"href":374,"dataGaName":375,"dataGaLocation":49},"/press/","press",{"text":377,"config":378,"lists":379},"Contact us",{"dataNavLevelOne":319},[380],{"items":381},[382,385,390],{"text":56,"config":383},{"href":58,"dataGaName":384,"dataGaLocation":49},"talk to sales",{"text":386,"config":387},"Get help",{"href":388,"dataGaName":389,"dataGaLocation":49},"/support/","get help",{"text":391,"config":392},"Customer portal",{"href":393,"dataGaName":394,"dataGaLocation":49},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":396,"login":397,"suggestions":404},"Close",{"text":398,"link":399},"To search repositories and projects, login to",{"text":400,"config":401},"gitlab.com",{"href":63,"dataGaName":402,"dataGaLocation":403},"search login","search",{"text":405,"default":406},"Suggestions",[407,409,413,415,419,423],{"text":78,"config":408},{"href":83,"dataGaName":78,"dataGaLocation":403},{"text":410,"config":411},"Code Suggestions (AI)",{"href":412,"dataGaName":410,"dataGaLocation":403},"/solutions/code-suggestions/",{"text":29,"config":414},{"href":130,"dataGaName":29,"dataGaLocation":403},{"text":416,"config":417},"GitLab on AWS",{"href":418,"dataGaName":416,"dataGaLocation":403},"/partners/technology-partners/aws/",{"text":420,"config":421},"GitLab on Google Cloud",{"href":422,"dataGaName":420,"dataGaLocation":403},"/partners/technology-partners/google-cloud-platform/",{"text":424,"config":425},"Why GitLab?",{"href":91,"dataGaName":424,"dataGaLocation":403},{"freeTrial":427,"mobileIcon":432,"desktopIcon":437,"secondaryButton":440},{"text":428,"config":429},"Start free trial",{"href":430,"dataGaName":54,"dataGaLocation":431},"https://gitlab.com/-/trials/new/","nav",{"altText":433,"config":434},"Gitlab Icon",{"src":435,"dataGaName":436,"dataGaLocation":431},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":433,"config":438},{"src":439,"dataGaName":436,"dataGaLocation":431},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":441,"config":442},"Get Started",{"href":443,"dataGaName":444,"dataGaLocation":431},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":446,"mobileIcon":450,"desktopIcon":452},{"text":447,"config":448},"Learn more about GitLab Duo",{"href":83,"dataGaName":449,"dataGaLocation":431},"gitlab duo",{"altText":433,"config":451},{"src":435,"dataGaName":436,"dataGaLocation":431},{"altText":433,"config":453},{"src":439,"dataGaName":436,"dataGaLocation":431},{"freeTrial":455,"mobileIcon":460,"desktopIcon":462},{"text":456,"config":457},"Back to pricing",{"href":209,"dataGaName":458,"dataGaLocation":431,"icon":459},"back to pricing","GoBack",{"altText":433,"config":461},{"src":435,"dataGaName":436,"dataGaLocation":431},{"altText":433,"config":463},{"src":439,"dataGaName":436,"dataGaLocation":431},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":469,"_dir":43,"_draft":6,"_partial":6,"_locale":7,"title":470,"button":471,"image":476,"config":480,"_id":482,"_type":35,"_source":37,"_file":483,"_stem":484,"_extension":40},"/shared/en-us/banner","is now in public beta!",{"text":472,"config":473},"Try the Beta",{"href":474,"dataGaName":475,"dataGaLocation":49},"/gitlab-duo/agent-platform/","duo banner",{"altText":477,"config":478},"GitLab Duo Agent Platform",{"src":479},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":481},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":486,"_dir":43,"_draft":6,"_partial":6,"_locale":7,"data":487,"_id":691,"_type":35,"title":692,"_source":37,"_file":693,"_stem":694,"_extension":40},"/shared/en-us/main-footer",{"text":488,"source":489,"edit":495,"contribute":500,"config":505,"items":510,"minimal":683},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":490,"config":491},"View page source",{"href":492,"dataGaName":493,"dataGaLocation":494},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":496,"config":497},"Edit this page",{"href":498,"dataGaName":499,"dataGaLocation":494},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":501,"config":502},"Please contribute",{"href":503,"dataGaName":504,"dataGaLocation":494},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":506,"facebook":507,"youtube":508,"linkedin":509},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[511,534,590,619,653],{"title":67,"links":512,"subMenu":517},[513],{"text":514,"config":515},"DevSecOps platform",{"href":76,"dataGaName":516,"dataGaLocation":494},"devsecops platform",[518],{"title":207,"links":519},[520,524,529],{"text":521,"config":522},"View plans",{"href":209,"dataGaName":523,"dataGaLocation":494},"view plans",{"text":525,"config":526},"Why Premium?",{"href":527,"dataGaName":528,"dataGaLocation":494},"/pricing/premium/","why premium",{"text":530,"config":531},"Why Ultimate?",{"href":532,"dataGaName":533,"dataGaLocation":494},"/pricing/ultimate/","why ultimate",{"title":535,"links":536},"Solutions",[537,542,544,546,551,556,560,563,567,572,574,577,580,585],{"text":538,"config":539},"Digital transformation",{"href":540,"dataGaName":541,"dataGaLocation":494},"/topics/digital-transformation/","digital transformation",{"text":153,"config":543},{"href":155,"dataGaName":153,"dataGaLocation":494},{"text":142,"config":545},{"href":125,"dataGaName":126,"dataGaLocation":494},{"text":547,"config":548},"Agile development",{"href":549,"dataGaName":550,"dataGaLocation":494},"/solutions/agile-delivery/","agile delivery",{"text":552,"config":553},"Cloud transformation",{"href":554,"dataGaName":555,"dataGaLocation":494},"/topics/cloud-native/","cloud transformation",{"text":557,"config":558},"SCM",{"href":138,"dataGaName":559,"dataGaLocation":494},"source code management",{"text":29,"config":561},{"href":130,"dataGaName":562,"dataGaLocation":494},"continuous integration & delivery",{"text":564,"config":565},"Value stream management",{"href":182,"dataGaName":566,"dataGaLocation":494},"value stream management",{"text":568,"config":569},"GitOps",{"href":570,"dataGaName":571,"dataGaLocation":494},"/solutions/gitops/","gitops",{"text":192,"config":573},{"href":194,"dataGaName":195,"dataGaLocation":494},{"text":575,"config":576},"Small business",{"href":199,"dataGaName":200,"dataGaLocation":494},{"text":578,"config":579},"Public sector",{"href":204,"dataGaName":205,"dataGaLocation":494},{"text":581,"config":582},"Education",{"href":583,"dataGaName":584,"dataGaLocation":494},"/solutions/education/","education",{"text":586,"config":587},"Financial services",{"href":588,"dataGaName":589,"dataGaLocation":494},"/solutions/finance/","financial services",{"title":212,"links":591},[592,594,596,598,601,603,605,607,609,611,613,615,617],{"text":224,"config":593},{"href":226,"dataGaName":227,"dataGaLocation":494},{"text":229,"config":595},{"href":231,"dataGaName":232,"dataGaLocation":494},{"text":234,"config":597},{"href":236,"dataGaName":237,"dataGaLocation":494},{"text":239,"config":599},{"href":241,"dataGaName":600,"dataGaLocation":494},"docs",{"text":262,"config":602},{"href":264,"dataGaName":5,"dataGaLocation":494},{"text":257,"config":604},{"href":259,"dataGaName":260,"dataGaLocation":494},{"text":266,"config":606},{"href":268,"dataGaName":269,"dataGaLocation":494},{"text":279,"config":608},{"href":281,"dataGaName":282,"dataGaLocation":494},{"text":271,"config":610},{"href":273,"dataGaName":274,"dataGaLocation":494},{"text":284,"config":612},{"href":286,"dataGaName":287,"dataGaLocation":494},{"text":289,"config":614},{"href":291,"dataGaName":292,"dataGaLocation":494},{"text":294,"config":616},{"href":296,"dataGaName":297,"dataGaLocation":494},{"text":299,"config":618},{"href":301,"dataGaName":302,"dataGaLocation":494},{"title":317,"links":620},[621,623,625,627,629,631,633,637,642,644,646,648],{"text":324,"config":622},{"href":326,"dataGaName":319,"dataGaLocation":494},{"text":329,"config":624},{"href":331,"dataGaName":332,"dataGaLocation":494},{"text":337,"config":626},{"href":339,"dataGaName":340,"dataGaLocation":494},{"text":342,"config":628},{"href":344,"dataGaName":345,"dataGaLocation":494},{"text":347,"config":630},{"href":349,"dataGaName":350,"dataGaLocation":494},{"text":352,"config":632},{"href":354,"dataGaName":355,"dataGaLocation":494},{"text":634,"config":635},"Sustainability",{"href":636,"dataGaName":634,"dataGaLocation":494},"/sustainability/",{"text":638,"config":639},"Diversity, inclusion and belonging (DIB)",{"href":640,"dataGaName":641,"dataGaLocation":494},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":357,"config":643},{"href":359,"dataGaName":360,"dataGaLocation":494},{"text":367,"config":645},{"href":369,"dataGaName":370,"dataGaLocation":494},{"text":372,"config":647},{"href":374,"dataGaName":375,"dataGaLocation":494},{"text":649,"config":650},"Modern Slavery Transparency Statement",{"href":651,"dataGaName":652,"dataGaLocation":494},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":654,"links":655},"Contact Us",[656,659,661,663,668,673,678],{"text":657,"config":658},"Contact an expert",{"href":58,"dataGaName":59,"dataGaLocation":494},{"text":386,"config":660},{"href":388,"dataGaName":389,"dataGaLocation":494},{"text":391,"config":662},{"href":393,"dataGaName":394,"dataGaLocation":494},{"text":664,"config":665},"Status",{"href":666,"dataGaName":667,"dataGaLocation":494},"https://status.gitlab.com/","status",{"text":669,"config":670},"Terms of use",{"href":671,"dataGaName":672,"dataGaLocation":494},"/terms/","terms of use",{"text":674,"config":675},"Privacy statement",{"href":676,"dataGaName":677,"dataGaLocation":494},"/privacy/","privacy statement",{"text":679,"config":680},"Cookie preferences",{"dataGaName":681,"dataGaLocation":494,"id":682,"isOneTrustButton":32},"cookie preferences","ot-sdk-btn",{"items":684},[685,687,689],{"text":669,"config":686},{"href":671,"dataGaName":672,"dataGaLocation":494},{"text":674,"config":688},{"href":676,"dataGaName":677,"dataGaLocation":494},{"text":679,"config":690},{"dataGaName":681,"dataGaLocation":494,"id":682,"isOneTrustButton":32},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[696],{"_path":697,"_dir":698,"_draft":6,"_partial":6,"_locale":7,"content":699,"config":703,"_id":705,"_type":35,"title":18,"_source":37,"_file":706,"_stem":707,"_extension":40},"/en-us/blog/authors/cesar-saavedra","authors",{"name":18,"config":700},{"headshot":701,"ctfId":702},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659600/Blog/Author%20Headshots/csaavedra1-headshot.jpg","csaavedra1",{"template":704},"BlogAuthor","content:en-us:blog:authors:cesar-saavedra.yml","en-us/blog/authors/cesar-saavedra.yml","en-us/blog/authors/cesar-saavedra",{"_path":709,"_dir":43,"_draft":6,"_partial":6,"_locale":7,"header":710,"eyebrow":711,"blurb":712,"button":713,"secondaryButton":717,"_id":719,"_type":35,"title":720,"_source":37,"_file":721,"_stem":722,"_extension":40},"/shared/en-us/next-steps","Start shipping better software faster","50%+ of the Fortune 100 trust GitLab","See what your team can do with the intelligent\n\n\nDevSecOps platform.\n",{"text":51,"config":714},{"href":715,"dataGaName":54,"dataGaLocation":716},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":56,"config":718},{"href":58,"dataGaName":59,"dataGaLocation":716},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1758326239940]