{"id":16179183,"url":"https://github.com/jonashackt/spring-boot-graalvm","last_synced_at":"2025-05-16T17:06:09.202Z","repository":{"id":37528715,"uuid":"248722958","full_name":"jonashackt/spring-boot-graalvm","owner":"jonashackt","description":"This example project shows how to compile a Webflux based Spring Boot application into a Native App using GraalVM Native Image locally \u0026 on GitHub Actions with \u0026 without Docker","archived":false,"fork":false,"pushed_at":"2025-04-24T15:39:15.000Z","size":1751,"stargazers_count":239,"open_issues_count":8,"forks_count":52,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-24T16:49:00.010Z","etag":null,"topics":["docker","github-actions","graalvm","graalvm-native-image","heroku","heroku-container-registry","heroku-docker","java","native-image","native-image-maven-plugin","spring-boot","spring-graal","substratevm","travis","travis-ci"],"latest_commit_sha":null,"homepage":"https://blog.codecentric.de/en/2020/05/spring-boot-graalvm/","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jonashackt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-03-20T10:02:07.000Z","updated_at":"2025-03-13T00:39:17.000Z","dependencies_parsed_at":"2024-01-20T00:24:21.667Z","dependency_job_id":"8611d277-7729-4a2d-8523-3aae4431f94e","html_url":"https://github.com/jonashackt/spring-boot-graalvm","commit_stats":{"total_commits":137,"total_committers":6,"mean_commits":"22.833333333333332","dds":0.291970802919708,"last_synced_commit":"4877f4f21ee4d2f45d3cb08ced75f6aaaf2ed788"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonashackt%2Fspring-boot-graalvm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonashackt%2Fspring-boot-graalvm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonashackt%2Fspring-boot-graalvm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonashackt%2Fspring-boot-graalvm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonashackt","download_url":"https://codeload.github.com/jonashackt/spring-boot-graalvm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254573588,"owners_count":22093731,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","github-actions","graalvm","graalvm-native-image","heroku","heroku-container-registry","heroku-docker","java","native-image","native-image-maven-plugin","spring-boot","spring-graal","substratevm","travis","travis-ci"],"created_at":"2024-10-10T05:25:49.212Z","updated_at":"2025-05-16T17:06:08.093Z","avatar_url":"https://github.com/jonashackt.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# spring-boot-graalvm\n[![Build Status](https://github.com/jonashackt/spring-boot-graalvm/workflows/native-image-compile/badge.svg)](https://github.com/jonashackt/spring-boot-graalvm/actions)\n[![License](http://img.shields.io/:license-mit-blue.svg)](https://github.com/jonashackt/spring-boot-graalvm/blob/master/LICENSE)\n[![renovateenabled](https://img.shields.io/badge/renovate-enabled-yellow)](https://renovatebot.com)\n[![versionspringboot](https://img.shields.io/badge/dynamic/xml?color=brightgreen\u0026url=https://raw.githubusercontent.com/jonashackt/spring-boot-graalvm/master/pom.xml\u0026query=%2F%2A%5Blocal-name%28%29%3D%27project%27%5D%2F%2A%5Blocal-name%28%29%3D%27parent%27%5D%2F%2A%5Blocal-name%28%29%3D%27version%27%5D\u0026label=springboot)](https://github.com/spring-projects/spring-boot)\n[![versionspring-graalvm-native](https://img.shields.io/badge/dynamic/xml?color=brightgreen\u0026url=https://raw.githubusercontent.com/jonashackt/spring-boot-graalvm/master/pom.xml\u0026query=%2F%2A%5Blocal-name%28%29%3D%27project%27%5D%2F%2A%5Blocal-name%28%29%3D%27properties%27%5D%2F%2A%5Blocal-name%28%29%3D%27spring-native.version%27%5D\u0026label=spring-native)](https://github.com/spring-projects-experimental/spring-graalvm-native)\n[![versionjava](https://img.shields.io/badge/graalvm_ce-21.2.0_JDK11-orange.svg?logo=java)](https://www.graalvm.org/)\n[![Deployed on Heroku](https://img.shields.io/badge/heroku-deployed-blueviolet.svg?logo=heroku\u0026)](https://spring-boot-graal.herokuapp.com/hello)\n[![Pushed to Docker Hub](https://img.shields.io/badge/docker_hub-released-blue.svg?logo=docker)](https://hub.docker.com/r/jonashackt/spring-boot-graalvm)\n\n\nThis example project shows how to compile a Webflux based Spring Boot application into a Native App using GraalVM Native Image\n\n\u003e This project here shows a technical demo of what's possible right now - stable GraalVM Native Image support for Spring Boot could be expected with [Spring Frameworks 5.3 release planned in October 2020](https://spring.io/blog/2019/12/03/spring-framework-maintenance-roadmap-in-2020-including-4-3-eol), on which Spring Boot 2.4 will be based.\n\n[![asciicast](https://asciinema.org/a/313688.svg)](https://asciinema.org/a/313688)\n\nA live deployment is available on Heroku: https://spring-boot-graal.herokuapp.com/hello\n\nThis project is used as example in some articles:\n\n* [blog.codecentric.de/en/2020/05/spring-boot-graalvm/](https://blog.codecentric.de/en/2020/05/spring-boot-graalvm/)\n* [blog.codecentric.de/en/2020/06/spring-boot-graalvm-docker-heroku/](https://blog.codecentric.de/en/2020/06/spring-boot-graalvm-docker-heroku/)\n* [blog.codecentric.de/en/2020/06/spring-boot-graalvm-native-image-maven-plugin/](https://blog.codecentric.de/en/2020/06/spring-boot-graalvm-native-image-maven-plugin/)\n\n[![javamagazin-092020-cover-small](screenshots/javamagazin-092020-cover-small.jpg)](https://public.centerdevice.de/41c5481e-5782-4c0e-bf7b-a62ec68d3854)\n\n## Table of Contents \n\n* [New to GraalVM with Spring Boot?](#new-to-graalvm-with-spring-boot)\n  * [Graal Native Image \u0026 SpringBoot](#graal-native-image--springboot)\n  * [Dynamic Graal Native Image configuration with @AutomaticFeature](#dynamic-graal-native-image-configuration-with-automaticfeature)\n* [Install GraalVM with SDKMAN](#install-graalvm-with-sdkman)\n  * [Install GraalVM Native Image](#install-graalvm-native-image)\n* [Create a simple WebFlux Reactive REST Spring Boot app](#create-a-simple-webflux-reactive-rest-spring-boot-app)\n* [Make Spring Boot app Graal Native Image friendly](#make-spring-boot-app-graal-native-image-friendly)\n  * [Relocate Annotation classpath scanning from runtime to build time](#relocate-annotation-classpath-scanning-from-runtime-to-build-time)\n  * [Disable usage of CGLIB proxies](#disable-usage-of-cglib-proxies)\n  * [Detect Autoconfiguration](#detect-autoconfiguration)\n  * [Get Spring Graal @AutomaticFeature](#get-spring-graal-automaticfeature)\n  * [Set start-class element in pom.xml](#set-start-class-element-in-pomxml)\n  * [Craft a compile.sh script](#craft-a-compilesh-script)\n  * [Run the compile.sh script \u0026 start your native Spring Boot App](#run-the-compilesh-script--start-your-native-spring-boot-app)\n* [Doing all the steps together using the native-image-maven-plugin](#doing-all-the-steps-together-using-the-native-image-maven-plugin)\n  * [Tackling the 'No default constructor found Failed to instantiate java.lang.NoSuchMethodException: io.jonashackt.springbootgraal.SpringBootHelloApplication.()' error](#tackling-the-no-default-constructor-found-failed-to-instantiate-javalangnosuchmethodexception-iojonashacktspringbootgraalspringboothelloapplication-error)\n* [Comparing Startup time \u0026 Memory footprint](#comparing-startup-time--memory-footprint)\n* [Build and Run your Native Image compilation on a Cloud-CI provider like TravisCI](#build-and-run-your-native-image-compilation-on-a-cloud-ci-provider-like-travisci)\n  * [Prevent the 'java.lang.UnsatisfiedLinkError: no netty_transport_native_epoll_x86_64 in java.library.path: [/usr/java/packages/lib, /usr/lib64, /lib64, /lib, /usr/lib]' error](#prevent-the-javalangunsatisfiedlinkerror-no-netty_transport_native_epoll_x86_64-in-javalibrarypath-usrjavapackageslib-usrlib64-lib64-lib-usrlib-error)\n  * [Tackling the 'There was an error linking the native image /usr/bin/ld: final link failed: Memory exhausted' error](#tackling-the-there-was-an-error-linking-the-native-image-usrbinld-final-link-failed-memory-exhausted-error)\n* [Build and Run your Native Image compilation on GitHub Actions](#build-and-run-your-native-image-compilation-on-github-actions)\n* [Use Docker to compile a Spring Boot App with GraalVM](#use-docker-to-compile-a-spring-boot-app-with-graalvm)\n  * [Tackling 'Exception java.lang.OutOfMemoryError in thread \"native-image pid watcher\"' error](#tackling-exception-javalangoutofmemoryerror-in-thread-native-image-pid-watcher-error)\n  * [Run Spring Boot Native Apps in Docker](#run-spring-boot-native-apps-in-docker)\n* [Running Spring Boot Graal Native Apps on Heroku](#running-spring-boot-graal-native-apps-on-heroku)\n  * [Configure the Spring Boot Native app's port dynamically inside a Docker container](#configure-the-spring-boot-native-apps-port-dynamically-inside-a-docker-container)\n  * [Use Docker to run our Spring Boot Native App on Heroku](#use-docker-to-run-our-spring-boot-native-app-on-heroku)\n  * [Work around the Heroku 512MB RAM cap: Building our Dockerimage with TravisCI](#work-around-the-heroku-512mb-ram-cap-building-our-dockerimage-with-travisci)\n  * [Tackling 'Error: Image build request failed with exit status 137' with the -J-Xmx parameter](#tackling-error-image-build-request-failed-with-exit-status-137-with-the--j-xmx-parameter)\n  * [Pushing and Releasing our Dockerized Native Spring Boot App on Heroku Container Infrastructure](#pushing-and-releasing-our-dockerized-native-spring-boot-app-on-heroku-container-infrastructure)\n  * [Pushing and Releasing our Dockerized Native Spring Boot App on Heroku Container Infrastructure using GitHub Actions](#pushing-and-releasing-our-dockerized-native-spring-boot-app-on-heroku-container-infrastructure-using-github-actions)\n* [Autorelease on Docker Hub with TravisCI](#autorelease-on-docker-hub-with-travisci)\n* [Links](#links)\n\n\n# New to GraalVM with Spring Boot?\n\nCurrent status of Spring's Graal support: \n\n* https://github.com/spring-projects/spring-framework/wiki/GraalVM-native-image-support\n* https://github.com/spring-projects/spring-framework/issues/22968\n\n\u003e Note: [GraalVM](https://www.graalvm.org/) is an umbrella for many projects - if we want to fasten the startup and reduce the footprint of our Spring Boot projects, we need to focus on [GraalVM Native Image](https://www.graalvm.org/docs/reference-manual/native-image/).  \n\n### Graal Native Image \u0026 SpringBoot\n\nThere are some good intro resources - like the [Running Spring Boot Applications as GraalVM Native Images talk @ Spring One Platform 2019](https://www.infoq.com/presentations/spring-boot-graalvm/) by [Andy Clement](https://twitter.com/andy_clement).\n\nOne could tell Native Image to initialize Java classes \n\n```\n# at build time:\nnative image --initialize-at-build-time=your.package.YourClass\n \n# or at runtime\nnative image --initialize-at-run-time=your.package.YourClass\n``` \n\nGraalVM Native Image supports:\n \n* __static configuration:__ via JSON files\n  * either hand-crafted or \n  * [generated by Graal Native Image agent](https://medium.com/graalvm/introducing-the-tracing-agent-simplifying-graalvm-native-image-configuration-c3b56c486271))\n* __dynamic configuration:__ with the help of a [Graal Feature interface](https://www.graalvm.org/sdk/javadoc/index.html?org/graalvm/nativeimage/hosted/Feature.html)\n  * implementing classes are called back throughout the image build process (see https://github.com/oracle/graal/blob/master/substratevm/REFLECTION.md#manual-configuration)\n\n### Dynamic Graal Native Image configuration with @AutomaticFeature\n\n[Andy Clement](https://twitter.com/andy_clement) also seems to lead a Spring experimental project, that provides a Graal @AutomaticFeature for typical Spring application: https://github.com/spring-projects-experimental/spring-graalvm-native\n\nThere are also already some example projects available: https://github.com/spring-projects-experimental/spring-graalvm-native/tree/master/spring-graalvm-native-samples \n\n\n# Install GraalVM with SDKMAN\n\nLet's install GraalVM with the help of SDKMAN. Therefore you need to [have SDKMAN itself installed](https://sdkman.io/install):\n\n```\ncurl -s \"https://get.sdkman.io\" | bash\nsource \"$HOME/.sdkman/bin/sdkman-init.sh\"\n```\n\nIf SDKMAN has been installed successfully, the following command should work:\n\n```\n$ sdk list java\n\n================================================================================\nAvailable Java Versions\n================================================================================\n Vendor        | Use | Version      | Dist    | Status     | Identifier\n--------------------------------------------------------------------------------\n AdoptOpenJDK  |     | 14.0.0.j9    | adpt    |            | 14.0.0.j9-adpt\n               |     | 14.0.0.hs    | adpt    |            | 14.0.0.hs-adpt\n               |     | 13.0.2.j9    | adpt    |            | 13.0.2.j9-adpt\n... \n GraalVM       | \u003e\u003e\u003e | 20.2.0.r11   | grl     | installed  | 20.2.0.r11-grl\n               |     | 20.2.0.r8    | grl     |            | 20.2.0.r8-grl\n               |     | 20.1.0.r11   | grl     |            | 20.1.0.r11-grl\n               |     | 20.1.0.r8    | grl     |            | 20.1.0.r8-grl\n               |     | 20.0.0.r11   | grl     |            | 20.0.0.r11-grl\n               |     | 20.0.0.r8    | grl     |            | 20.0.0.r8-grl\n               |     | 19.3.1.r11   | grl     |            | 19.3.1.r11-grl\n               |     | 19.3.1.r8    | grl     |            | 19.3.1.r8-grl\n...\n```\n\nThe list itself is much longer and you could see the wonderful simplicity of this approach: Don't ever mess again with JDK installations!\n\nNow to install GraalVM based on JDK11, simply run:\n\n```\nsdk install java 20.2.0.r11-grl\n``` \n\nSDKMAN now installs GraalVM for us. To have the correct `PATH` configuration in place, you may need to restart your console. If everything went fine, you should see `java -version` react like this:\n\n```\n$ java -version\nopenjdk version \"11.0.8\" 2020-07-14\nOpenJDK Runtime Environment GraalVM CE 20.2.0 (build 11.0.8+10-jvmci-20.2-b03)\nOpenJDK 64-Bit Server VM GraalVM CE 20.2.0 (build 11.0.8+10-jvmci-20.2-b03, mixed mode, sharing)\n```\n\n\n\n### Install GraalVM Native Image\n\nGraalVM brings a special tool `gu` - the GraalVM updater. To list everything thats currently installed, run\n\n```\n$ gu list\nComponentId              Version             Component name      Origin\n--------------------------------------------------------------------------------\ngraalvm                  20.2.0              GraalVM Core\n```\n\nNow to install GraalVM Native image, simply run:\n\n```\ngu install native-image\n```\n\nAfter that, the `native-image` command should work for you:\n\n```\n$ native-image --version\nGraalVM Version 20.2.0 (Java Version 11.0.8)\n```\n\n\n# Create a simple WebFlux Reactive REST Spring Boot app\n\nAs famous [starbuxman](https://twitter.com/starbuxman) suggests, we start at: https://start.spring.io/!\n\nAs https://github.com/spring-projects/spring-framework/wiki/GraalVM-native-image-support suggests, the GraalVM Native Image support becomes better every day - so [we should choose the newest Spring Boot `2.3` Milestone release](https://github.com/spring-projects-experimental/spring-graalvm-native) available:\n\n\u003e Spring Boot 2.3.0.M1 (you may be able to get some things working with Boot 2.2.X but not 2.1 or earlier)\n\n![spring.start.io](screenshots/spring.start.io.png)      \n\nStable Native Image support for Spring Boot could be expected with [Spring Frameworks 5.3 release planned in October 2020](https://spring.io/blog/2019/12/03/spring-framework-maintenance-roadmap-in-2020-including-4-3-eol), on which Spring Boot 2.4 will be based.\n\nLet's create a simple Spring Boot Reactive REST service. First we need a Handler like [HelloHandler](src/main/java/io/jonashackt/springbootgraal/HelloHandler.java):\n\n```java\npackage io.jonashackt.springbootgraal;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.reactive.function.server.ServerRequest;\nimport org.springframework.web.reactive.function.server.ServerResponse;\nimport reactor.core.publisher.Mono;\n\n@Component\npublic class HelloHandler {\n\n    protected static String RESPONSE_TEXT= \"Hello Reactive People!\";\n\n    public Mono\u003cServerResponse\u003e hello(ServerRequest serverRequest) {\n        return ServerResponse\n                        .ok()\n                        .contentType(MediaType.TEXT_PLAIN)\n                        .body(BodyInserters.fromValue(RESPONSE_TEXT));\n    }\n}\n```\n\nIn the Reactive Spring approach we also need a Router - let's create [HelloRouter](src/main/java/io/jonashackt/springbootgraal/HelloRouter.java):\n\n```java\npackage io.jonashackt.springbootgraal;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.reactive.function.server.*;\n\n@Component\npublic class HelloRouter {\n\n    @Bean\n    public RouterFunction\u003cServerResponse\u003e route(HelloHandler helloHandler) {\n        return RouterFunctions.route(\n                RequestPredicates.GET(\"/hello\").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),\n                serverRequest -\u003e helloHandler.hello(serverRequest)\n        );\n    }\n}\n```\n\nNow we have everything in place to create a Testcase [HelloRouterTest](src/test/java/io/jonashackt/springbootgraal/HelloRouterTest.java) using the non-blocking [WebClient](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html):\n\n```java\npackage io.jonashackt.springbootgraal;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.http.MediaType;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\nclass HelloRouterTest {\n\n\t@Test void\n\tshould_call_reactive_rest_resource(@Autowired WebTestClient webTestClient) {\n\t\twebTestClient.get().uri(\"/hello\")\n\t\t\t.accept(MediaType.TEXT_PLAIN)\n\t\t\t.exchange()\n\t\t\t.expectBody(String.class).isEqualTo(HelloHandler.RESPONSE_TEXT);\n\t}\n}\n``` \n\nIf you want to create another Spring Boot app I can recomment the great [Getting Started Guides](https://spring.io/guides)!\n\n\n\n# Make Spring Boot app Graal Native Image friendly\n\nFrom https://github.com/spring-projects/spring-framework/wiki/GraalVM-native-image-support#experimental-support:\n\n\u003e \"The spring-graalvm-native experimental project, created by Andy Clement, shows how it is possible to run a Spring Boot application out of the box as a GraalVM native image. It could be used as a basis for a potential upcoming official support.\"\n\nSo let's try this currently available implementation!\n\n\n### Relocate Annotation classpath scanning from runtime to build time\n\nThe `spring-context-indexer` is an Annotation processor, which pushes the scan for Annotations from runtime to build time - see the docs: https://docs.spring.io/spring/docs/5.2.4.RELEASE/spring-framework-reference/core.html#beans-scanning-index:\n\n\u003e While classpath scanning is very fast, it is possible to improve the startup performance of large applications by creating a static list of candidates at compilation time. In this mode, all modules that are target of component scan must use this mechanism.\n\nWe could use the spring-context-indexer via importing it with Maven:\n\n```xml\n\u003cdependencies\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003eorg.springframework\u003c/groupId\u003e\n        \u003cartifactId\u003espring-context-indexer\u003c/artifactId\u003e\n        \u003coptional\u003etrue\u003c/optional\u003e\n    \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\nThis would produce a `META-INF/spring.components` file containing a list of all Spring Compontens, Entities and so on.\n\n__But we don't have to do this manually__, since the [Spring Graal @AutomaticFeature](https://github.com/spring-projects-experimental/spring-graalvm-native) (again, this is in experimental stage right now) does this automatically for us.\n\nThe `@AutomaticFeature` will additionally chase down imported annotated classes like `@Import` - it knows, which kinds of annotations lead to reflection needs at runtime, which with GraalVM need to be registered at build time.\n\nAnd as resource files like `application.properties` also need to be registered at build time, the Feature covers those too. \n\n\n### Disable usage of CGLIB proxies\n\nWith Spring Boot 2.2 CGLIB proxies are no longer necessary - it introduces the new `proxyBeanMethods` option to avoid CGLIB processing. Let's have a look at our [SpringBootHelloApplication.java](src/main/java/io/jonashackt/springbootgraal/SpringBootHelloApplication.java):\n\n```java\n@SpringBootApplication(proxyBeanMethods = false)\npublic class SpringBootHelloApplication {\n    ...\n}\n```\n\nThe usage of [JDK Proxies is supported by GraalVM](https://github.com/oracle/graal/blob/master/substratevm/DYNAMIC_PROXY.md), they just need to be registered at build time. This is also taken care of by the [Spring Graal @AutomaticFeature](https://github.com/spring-projects-experimental/spring-graalvm-native).\n\n\n### Detect Autoconfiguration\n\nSpring Boot ships with lot's of autoconfiguration projects, which only kick in, when there are specific classes found on the class path. Since this is done at runtime, it wouldn't work with GraalVM.\n\nBut the [SpringBootHelloApplication.java](src/main/java/io/jonashackt/springbootgraal/SpringBootHelloApplication.java) also takes care of this. It simply analyses the `META-INF/spring.factories` file, where the autoconfiguration classes are listed. An [example of such a file](https://github.com/codecentric/cxf-spring-boot-starter/blob/master/cxf-spring-boot-starter/src/main/resources/META-INF/spring.factories) could be found in the community-driven Spring Boot Starter [cxf-spring-boot-starter](https://github.com/codecentric/cxf-spring-boot-starter).\n\nThe `@AutomaticFeature` again pulls the work from runtime to build time - and eliminates the need for runtime autoconfiguration.\n\n\n### Get Spring Graal @AutomaticFeature\n\nIn order to compile our Spring Boot App as a Native Image, we need to have the latest [Spring Graal @AutomaticFeature](https://github.com/spring-projects-experimental/spring-graalvm-native) in place. As until March 2020 there was no Maven Dependency available, since this project is in a very early stage of development I guess. So I initially crafted a script `get-spring-feature.sh` that cloned and build the project for local usage.\n\nBut the Spring guys are moving fast! As there was also [a spring.io post released by starbuxman](https://spring.io/blog/2020/04/16/spring-tips-the-graalvm-native-image-builder-feature) at 16th of April, I think he got [Andy Clement](https://twitter.com/andy_clement) and [Sébastien Deleuze](https://twitter.com/sdeleuze) to get him a Maven dependecy available on https://repo.spring.io/milestone :)\n\nSo there we go! Now we don't need to manually download and compile the @AutomaticFeature, we simply add a dependency to our [pom.xml](pom.xml):\n\n```\n\t\u003cdependencies\u003e\n\t\t\u003cdependency\u003e\n\t\t\t\u003cgroupId\u003eorg.springframework.experimental\u003c/groupId\u003e\n\t\t\t\u003cartifactId\u003espring-graalvm-native\u003c/artifactId\u003e\n\t\t\t\u003cversion\u003e0.7.1\u003c/version\u003e\n\t\t\u003c/dependency\u003e\n    ...\n\n\t\u003crepositories\u003e\n\t\t\u003crepository\u003e\n\t\t\t\u003cid\u003espring-milestones\u003c/id\u003e\n\t\t\t\u003cname\u003eSpring Milestones\u003c/name\u003e\n\t\t\t\u003curl\u003ehttps://repo.spring.io/milestone\u003c/url\u003e\n\t\t\u003c/repository\u003e\n\t\u003c/repositories\u003e\n\t\u003cpluginRepositories\u003e\n\t\t\u003cpluginRepository\u003e\n\t\t\t\u003cid\u003espring-milestones\u003c/id\u003e\n\t\t\t\u003cname\u003eSpring Milestones\u003c/name\u003e\n\t\t\t\u003curl\u003ehttps://repo.spring.io/milestone\u003c/url\u003e\n\t\t\u003c/pluginRepository\u003e\n\t\u003c/pluginRepositories\u003e\n\n```\n\nBe sure to also have the separate `Spring Milestones` repository definition in place, since the library isn't available on Maven Central right now!\n\n\n### Set start-class element in pom.xml\n\nFor successfully being able to execute the `native-image` compilation process, we need to provide the command with the full name of our Spring Boot main class. \n\nAt first I provided a parameter for my `compile.sh` script we have a look into later on. But as the [native-image-maven-plugin](https://mvnrepository.com/artifact/com.oracle.substratevm/native-image-maven-plugin) also relies on this setting, I found it rather okay to provide this class' name inside the [pom.xml](pom.xml):\n\n```\n\t\u003cproperties\u003e\n\t\t...\n\t\t\u003cstart-class\u003eio.jonashackt.springbootgraal.SpringBootHelloApplication\u003c/start-class\u003e\n\t\u003c/properties\u003e\n```\n\nSince after setting this class once in our `pom.xml`, we don't need to bother with this parameter again - since we could read it from our pom in the later steps automatically.\n\n\n### Craft a compile.sh script\n\nI'am pretty sure, that this step described here will not be necessary when Spring will officially release the Graal full support. But right now, we do need to do a little grunt work here.\n\nThere are great examples of working compile scripts inside the [spring-graalvm-native-samples](https://github.com/spring-projects-experimental/spring-graalvm-native/tree/master/spring-graalvm-native-samples) project. So let's try to derive our own from that - just have a look into this project's [compile.sh](compile.sh):\n\n```shell script\n#!/usr/bin/env bash\n\necho \"[--\u003e] Detect artifactId from pom.xml\"\nARTIFACT=$(mvn -q \\\n-Dexec.executable=echo \\\n-Dexec.args='${project.artifactId}' \\\n--non-recursive \\\nexec:exec);\necho \"artifactId is '$ARTIFACT'\"\n\necho \"[--\u003e] Detect artifact version from pom.xml\"\nVERSION=$(mvn -q \\\n  -Dexec.executable=echo \\\n  -Dexec.args='${project.version}' \\\n  --non-recursive \\\n  exec:exec);\necho \"artifact version is '$VERSION'\"\n\necho \"[--\u003e] Detect Spring Boot Main class ('start-class') from pom.xml\"\nMAINCLASS=$(mvn -q \\\n-Dexec.executable=echo \\\n-Dexec.args='${start-class}' \\\n--non-recursive \\\nexec:exec);\necho \"Spring Boot Main class ('start-class') is '$MAINCLASS'\"\n```\n\nThe first part of the script is dedicated to define needed variables for later GraalVM Native Image compilation. The variables `ARTIFACT`, `VERSION` and `MAINCLASS` could be simply derived from our [pom.xml](pom.xml) with [the help of the Maven exec plugin](https://stackoverflow.com/a/26514030/4964553).\n\nIn the next section of the [compile.sh](compile.sh) script, we clean (aka remove) the `target` directory and build our Spring Boot App via a well known `mvn package`:\n\n```shell script\necho \"[--\u003e] Cleaning target directory \u0026 creating new one\"\nrm -rf target\nmkdir -p target/native-image\n\necho \"[--\u003e] Build Spring Boot App with mvn package\"\nmvn -DskipTests package\n```\n\n\nAfter the build, the Spring Boot fat jar needs to be expanded and the classpath needs to be set to the content of the results.\n\nAlso the Spring Graal AutomaticFeature needs to be available on the classpath. This is taken care by using the all the libraries found in `BOOT-INF/lib`, since by using the Maven dependency of `spring-graalvm-native` the automatic feature also resides there. \n\n```shell script\necho \"[--\u003e] Expanding the Spring Boot fat jar\"\nJAR=\"$ARTIFACT-$VERSION.jar\"\ncd target/native-image\njar -xvf ../$JAR \u003e/dev/null 2\u003e\u00261\ncp -R META-INF BOOT-INF/classes\n\necho \"[--\u003e] Set the classpath to the contents of the fat jar (where the libs contain the Spring Graal AutomaticFeature)\"\nLIBPATH=`find BOOT-INF/lib | tr '\\n' ':'`\nCP=BOOT-INF/classes:$LIBPATH\n``` \n\nNow finally the GraalVM Native Image compilation is triggered with lot's of appropriate configuration options:\n\n```shell script\nGRAALVM_VERSION=`native-image --version`\necho \"[--\u003e] Compiling Spring Boot App '$ARTIFACT' with $GRAALVM_VERSION\"\ntime native-image \\\n  -H:+TraceClassInitialization \\\n  -H:Name=$ARTIFACT \\\n  -H:+ReportExceptionStackTraces \\\n  -Dspring.graal.remove-unused-autoconfig=true \\\n  -Dspring.graal.remove-yaml-support=true \\\n  -cp $CP $MAINCLASS;\n```\n\nI altered this section compared to the example scripts also, since I wanted to see the compilation process in my console.\n\n\n### Run the compile.sh script \u0026 start your native Spring Boot App\n\nWe can now run the compile script with: \n\n```shell script\n./compile.sh\n```\n\nThe compile step does take it's time (depending on your hardware!). On my MacBook Pro 2017 this takes around 3 to 4 minutes. I prepared a small asciinema record so that you can have a look at how the compilation process works:\n\n[![asciicast](https://asciinema.org/a/320745.svg)](https://asciinema.org/a/320745)\n\n\nIf your console shows something like the following:\n\n```shell script\n[spring-boot-graal:93927]   (typeflow):  74,606.04 ms, 12.76 GB\n[spring-boot-graal:93927]    (objects):  58,480.01 ms, 12.76 GB\n[spring-boot-graal:93927]   (features):   8,413.90 ms, 12.76 GB\n[spring-boot-graal:93927]     analysis: 147,776.93 ms, 12.76 GB\n[spring-boot-graal:93927]     (clinit):   1,578.42 ms, 12.76 GB\n[spring-boot-graal:93927]     universe:   4,909.40 ms, 12.76 GB\n[spring-boot-graal:93927]      (parse):   6,885.61 ms, 12.78 GB\n[spring-boot-graal:93927]     (inline):   6,594.06 ms, 12.78 GB\n[spring-boot-graal:93927]    (compile):  33,040.00 ms, 12.79 GB\n[spring-boot-graal:93927]      compile:  50,001.85 ms, 12.79 GB\n[spring-boot-graal:93927]        image:   8,963.82 ms, 12.79 GB\n[spring-boot-graal:93927]        write:   2,414.18 ms, 12.79 GB\n[spring-boot-graal:93927]      [total]: 232,479.88 ms, 12.79 GB\n\nreal\t3m54.635s\nuser\t16m16.765s\nsys\t1m55.756s\n```\n \nyou're now be able to __fire up your first GraalVM Native App!__. How cool is that?!! All you have to do is to run the generated executable `/target/native-image/spring-graal-vm`:\n\n```shell script\n$ ./target/native-image/spring-graal-vm\n\n  .   ____          _            __ _ _\n /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\\n( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\\n \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )\n  '  |____| .__|_| |_|_| |_\\__, | / / / /\n =========|_|==============|___/=/_/_/_/\n :: Spring Boot ::\n\n2020-03-26 15:45:32.086  INFO 33864 --- [           main] i.j.s.SpringBootHelloApplication         : Starting SpringBootHelloApplication on PikeBook.fritz.box with PID 33864 (/Users/jonashecht/dev/spring-boot/spring-boot-graalvm/target/spring-boot-graal started by jonashecht in /Users/jonashecht/dev/spring-boot/spring-boot-graalvm/target)\n2020-03-26 15:45:32.086  INFO 33864 --- [           main] i.j.s.SpringBootHelloApplication         : No active profile set, falling back to default profiles: default\n2020-03-26 15:45:32.133  WARN 33864 --- [           main] io.netty.channel.DefaultChannelId        : Failed to find the current process ID from ''; using a random value: 801435406\n2020-03-26 15:45:32.136  INFO 33864 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080\n2020-03-26 15:45:32.137  INFO 33864 --- [           main] i.j.s.SpringBootHelloApplication         : Started SpringBootHelloApplication in 0.083 seconds (JVM running for 0.086)\n```\n\nI also prepared a small asciicast - but be aware, you'll maybe don't get it since it's damn fast :)\n\n[![asciicast](https://asciinema.org/a/313688.svg)](https://asciinema.org/a/313688)\n\n__Your Spring Boot App started in 0.083!!__ Simply access the App via http://localhost:8080/hello.\n\n\n\n# Doing all the steps together using the native-image-maven-plugin\n\nCurrently it really makes sense to hand-craft a bash script like our [compile.sh](compile.sh) in order to be able to debug all those `native-image` options!\n\nBut the development of GraalVM and the spring-graalvm-native projects really go fast. See [this post about GraalVM 20.1.0 release](https://medium.com/graalvm/graalvm-20-1-7ce7e89f066b) for example. So it makes also sense to have a look at the posibility to do all the needed steps to compile a Spring Boot app with GraalVM native images by only using the [native-image-maven-plugin](https://search.maven.org/search?q=g:org.graalvm.nativeimage%20AND%20a:native-image-maven-plugin).\n\n\u003e For more information about the `native-image-maven-plugin` see this post: https://medium.com/graalvm/simplifying-native-image-generation-with-maven-plugin-and-embeddable-configuration-d5b283b92f57\n\nTherefor let's add a new Maven profile to our [pom.xml](pom.xml) as [described in the spring-graalvm-native docs](https://repo.spring.io/milestone/org/springframework/experimental/spring-graalvm-native-docs/0.7.0/spring-graalvm-native-docs-0.7.0.zip!/reference/index.html#_add_the_maven_plugin):\n\n```xml\n\t\u003cprofiles\u003e\n\t\t\u003cprofile\u003e\n\t\t\t\u003cid\u003enative\u003c/id\u003e\n\t\t\t\u003cbuild\u003e\n\t\t\t\t\u003cplugins\u003e\n\t\t\t\t\t\u003cplugin\u003e\n\t\t\t\t\t\t\u003cgroupId\u003eorg.graalvm.nativeimage\u003c/groupId\u003e\n\t\t\t\t\t\t\u003cartifactId\u003enative-image-maven-plugin\u003c/artifactId\u003e\n\t\t\t\t\t\t\u003cversion\u003e20.2.0\u003c/version\u003e\n\t\t\t\t\t\t\u003cconfiguration\u003e\n\t\t\t\t\t\t\t\u003cbuildArgs\u003e-J-Xmx4G -H:+TraceClassInitialization -H:+ReportExceptionStackTraces -Dspring.graal.remove-unused-autoconfig=true -Dspring.graal.remove-yaml-support=true\u003c/buildArgs\u003e\n\t\t\t\t\t\t\t\u003cimageName\u003e${project.artifactId}\u003c/imageName\u003e\n\t\t\t\t\t\t\u003c/configuration\u003e\n\t\t\t\t\t\t\u003cexecutions\u003e\n\t\t\t\t\t\t\t\u003cexecution\u003e\n\t\t\t\t\t\t\t\t\u003cgoals\u003e\n\t\t\t\t\t\t\t\t\t\u003cgoal\u003enative-image\u003c/goal\u003e\n\t\t\t\t\t\t\t\t\u003c/goals\u003e\n\t\t\t\t\t\t\t\t\u003cphase\u003epackage\u003c/phase\u003e\n\t\t\t\t\t\t\t\u003c/execution\u003e\n\t\t\t\t\t\t\u003c/executions\u003e\n\t\t\t\t\t\u003c/plugin\u003e\n\t\t\t\t\t\u003cplugin\u003e\n\t\t\t\t\t\t\u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n\t\t\t\t\t\t\u003cartifactId\u003espring-boot-maven-plugin\u003c/artifactId\u003e\n\t\t\t\t\t\u003c/plugin\u003e\n\t\t\t\t\u003c/plugins\u003e\n\t\t\t\u003c/build\u003e\n\t\t\u003c/profile\u003e\n\t\u003c/profiles\u003e\n```\n\nThe `buildArgs` tag is crucial here! We need to configure everything needed to successfully run a `native-image` command for our Spring Boot app as already used inside our [compile.sh](compile.sh).\n\nBut we can leave out `-cp $CP $MAINCLASS` parameter since they are already provided by the plugin. Remember now we run the `native-image` compilation from within the Maven pom context where all those is known.\n\nUsing the `\u003cimageName\u003e${project.artifactId}\u003c/imageName\u003e` is a good idea in order to use our `artifactId` for the resulting executable image name. Otherwise we end up with a fully qualified class name like `io.jonashackt.springbootgraal.springboothelloapplication`.\n\nJust remember to have the `start-class` property in place:\n\n```\n\u003cproperties\u003e\n\t\t\u003cstart-class\u003eio.jonashackt.springbootgraal.SpringBootHelloApplication\u003c/start-class\u003e\n        ...\n\u003c/properties\u003e\n```\n\nThat should already suffice! Now we can simply run our Maven profile with:\n\n```\nmvn -Pnative clean package\n```\n\n\n### Tackling the 'No default constructor found Failed to instantiate java.lang.NoSuchMethodException: io.jonashackt.springbootgraal.SpringBootHelloApplication.\u003cinit\u003e()' error\n\nAfter executing the build process (which went fine), the resulting native image doesn't start without errors:\n\n```\n./spring-boot-graal\n\n  .   ____          _            __ _ _\n /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\\n( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\\n \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )\n  '  |____| .__|_| |_|_| |_\\__, | / / / /\n =========|_|==============|___/=/_/_/_/\n :: Spring Boot ::\n\nJun 05, 2020 10:46:27 AM org.springframework.boot.StartupInfoLogger logStarting\nINFO: Starting application on PikeBook.fritz.box with PID 33047 (started by jonashecht in /Users/jonashecht/dev/spring-boot/spring-boot-graalvm/target)\nJun 05, 2020 10:46:27 AM org.springframework.boot.SpringApplication logStartupProfileInfo\nINFO: No active profile set, falling back to default profiles: default\nJun 05, 2020 10:46:27 AM org.springframework.context.support.AbstractApplicationContext refresh\nWARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springBootHelloApplication': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.jonashackt.springbootgraal.SpringBootHelloApplication]: No default constructor found; nested exception is java.lang.NoSuchMethodException: io.jonashackt.springbootgraal.SpringBootHelloApplication.\u003cinit\u003e()\nJun 05, 2020 10:46:27 AM org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener logMessage\nINFO:\n\nError starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.\nJun 05, 2020 10:46:27 AM org.springframework.boot.SpringApplication reportFailure\nSEVERE: Application run failed\norg.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springBootHelloApplication': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.jonashackt.springbootgraal.SpringBootHelloApplication]: No default constructor found; nested exception is java.lang.NoSuchMethodException: io.jonashackt.springbootgraal.SpringBootHelloApplication.\u003cinit\u003e()\n\tat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1320)\n\tat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1214)\n\tat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)\n\tat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)\n\tat org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)\n\tat org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)\n\tat org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)\n\tat org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)\n\tat org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:895)\n\tat org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)\n\tat org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)\n\tat org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:62)\n\tat org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758)\n\tat org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750)\n\tat org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)\n\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:315)\n\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:1237)\n\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)\n\tat io.jonashackt.springbootgraal.SpringBootHelloApplication.main(SpringBootHelloApplication.java:10)\nCaused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.jonashackt.springbootgraal.SpringBootHelloApplication]: No default constructor found; nested exception is java.lang.NoSuchMethodException: io.jonashackt.springbootgraal.SpringBootHelloApplication.\u003cinit\u003e()\n\tat org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:83)\n\tat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1312)\n\t... 18 more\nCaused by: java.lang.NoSuchMethodException: io.jonashackt.springbootgraal.SpringBootHelloApplication.\u003cinit\u003e()\n\tat java.lang.Class.getConstructor0(DynamicHub.java:3349)\n\tat java.lang.Class.getDeclaredConstructor(DynamicHub.java:2553)\n\tat org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:78)\n\t... 19 more\n```\n\n\u003e But what is the difference between the way our [compile.sh](compile.sh) works compared to the `native-image-maven-plugin` really? The parameters are the same!\n\nI had a hard time figuring that one out! But finally I found a difference - it's all about the Spring Feature computed `spring.components`:\n\n```\n$ ./compile.sh\n...\nExcluding 104 auto-configurations from spring.factories file\nFound no META-INF/spring.components -\u003e synthesizing one...\nComputed spring.components is\nvvv\nio.jonashackt.springbootgraal.HelloRouter=org.springframework.stereotype.Component\nio.jonashackt.springbootgraal.HelloHandler=org.springframework.stereotype.Component\nio.jonashackt.springbootgraal.SpringBootHelloApplication=org.springframework.stereotype.Component\n^^^\nRegistered 3 entries\nConfiguring initialization time for specific types and packages:\n#69 buildtime-init-classes   #21 buildtime-init-packages   #28 runtime-init-classes    #0 runtime-init-packages\n```\n\nwith our [compile.sh](compile.sh) the Feature finds the 3 classes that are Spring Components and thus are relevant for our Application to work.\n\n```\n$ mvn -Pnative clean package\n...\nExcluding 104 auto-configurations from spring.factories file\nFound no META-INF/spring.components -\u003e synthesizing one...\nComputed spring.components is\nvvv\n^^^\nRegistered 0 entries\nConfiguring initialization time for specific types and packages:\n#69 buildtime-init-classes   #21 buildtime-init-packages   #28 runtime-init-classes    #0 runtime-init-packages\n```\n\nOur Maven plugin does not recognize the three needed classes! And thus it also doesn't successfully run our application in the end, since the REST controller doesn't work, if we access it via http://localhost:8080/hello\n\nIn a non-native world, our Spring Components would be explored at runtime via component scanning. But with GraalVM native image compilation, all notion of a thing called classpath is lost at runtime! So we need something to do the component scanning at build time.\nThe one utility that does this is the [spring-context-indexer](https://stackoverflow.com/questions/47254907/how-can-i-create-a-spring-5-component-index/48407939) and is executed by the Spring @AutomaticFeature for us, if we use our `compile.sh`.\n\nBut using the `native-image-maven-plugin` this isn't done automatically! So we have to explicitely include the [spring-context-indexer](https://mvnrepository.com/artifact/org.springframework/spring-context-indexer/5.2.6.RELEASE) dependency inside our [pom.xml]:\n\n```xml\n\t\t\u003cdependency\u003e\n\t\t\t\u003cgroupId\u003eorg.springframework\u003c/groupId\u003e\n\t\t\t\u003cartifactId\u003espring-context-indexer\u003c/artifactId\u003e\n\t\t\u003c/dependency\u003e\n```\n\nNow running a Maven build, the file `target/classes/META_INF/spring.components` containing our 3 needed classes is created:\n\n```\nio.jonashackt.springbootgraal.HelloHandler=org.springframework.stereotype.Component\nio.jonashackt.springbootgraal.HelloRouter=org.springframework.stereotype.Component\nio.jonashackt.springbootgraal.SpringBootHelloApplication=org.springframework.stereotype.Component\n```\n\nAnd using that dependency, our Maven build finally works as expected:\n\n```\n$ mvn -Pnative clean package\n...\nExcluding 104 auto-configurations from spring.factories file\nProcessing META-INF/spring.components files...\nRegistered 3 entries\nConfiguring initialization time for specific types and packages:\n#69 buildtime-init-classes   #21 buildtime-init-packages   #28 runtime-init-classes    #0 runtime-init-packages\n...\n```\n\n__The question remains why the Spring @AutomaticFeature doesn't do that automatically only while executed via the `native-image-maven-plugin`!__\n\n\n# Comparing Startup time \u0026 Memory footprint \n\nOk, the initial goal was to run our beloved Spring Boot Apps at lightning speed. Now we have a \"normal\" Spring Boot App, that we're able to run with:\n\n```\n$ java -jar target/spring-boot-graal-0.0.1-SNAPSHOT.jar\n\n  .   ____          _            __ _ _\n /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\\n( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\\n \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )\n  '  |____| .__|_| |_|_| |_\\__, | / / / /\n =========|_|==============|___/=/_/_/_/\n :: Spring Boot ::             (v2.3.0.M4)\n\n2020-04-30 15:40:21.187  INFO 40149 --- [           main] i.j.s.SpringBootHelloApplication         : Starting SpringBootHelloApplication v0.0.1-SNAPSHOT on PikeBook.fritz.box with PID 40149 (/Users/jonashecht/dev/spring-boot/spring-boot-graalvm/target/spring-boot-graal-0.0.1-SNAPSHOT.jar started by jonashecht in /Users/jonashecht/dev/spring-boot/spring-boot-graalvm)\n2020-04-30 15:40:21.190  INFO 40149 --- [           main] i.j.s.SpringBootHelloApplication         : No active profile set, falling back to default profiles: default\n2020-04-30 15:40:22.280  INFO 40149 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080\n2020-04-30 15:40:22.288  INFO 40149 --- [           main] i.j.s.SpringBootHelloApplication         : Started SpringBootHelloApplication in 1.47 seconds (JVM running for 1.924)\n```\n\nThe standard way takes about `1.47 seconds` to start up and it uses around `491 MB` of RAM:\n\n```\n  PID TTY           TIME CMD\nProcesses: 545 total, 2 running, 1 stuck, 542 sleeping, 2943 threads                                                                                                     16:21:23\nLoad Avg: 1.35, 1.92, 2.30  CPU usage: 3.96% user, 3.84% sys, 92.19% idle  SharedLibs: 240M resident, 63M data, 19M linkedit.\nMemRegions: 224056 total, 3655M resident, 50M private, 6794M shared. PhysMem: 16G used (3579M wired), 93M unused.\nVM: 2744G vsize, 1997M framework vsize, 64447396(189) swapins, 66758016(0) swapouts. Networks: packets: 34854978/40G in, 30746488/34G out.\nDisks: 28626843/545G read, 11039646/423G written.\n\nPID    COMMAND      %CPU TIME     #TH  #WQ  #POR MEM  PURG CMPR PGRP  PPID STATE    BOOSTS    %CPU_ME %CPU_OTHRS UID  FAULTS  COW  MSGS MSGR SYSBSD SYSM CSW    PAGE IDLE POWE\n40862  java         0.1  00:05.46 27   1    112  491M 0B   0B   40862 1592 sleeping *0[1]     0.00000 0.00000    501  136365  1942 5891 2919 52253+ 8577 21848+ 7148 733+ 0.8\n```\n\nNow comparing our Natively compiled Spring Boot App, we see a startup time of about `0.078 seconds`:\n\n```\n./spring-boot-graal\n\n  .   ____          _            __ _ _\n /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\\n( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\\n \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )\n  '  |____| .__|_| |_|_| |_\\__, | / / / /\n =========|_|==============|___/=/_/_/_/\n :: Spring Boot ::\n\n2020-05-01 10:25:31.200  INFO 42231 --- [           main] i.j.s.SpringBootHelloApplication         : Starting SpringBootHelloApplication on PikeBook.fritz.box with PID 42231 (/Users/jonashecht/dev/spring-boot/spring-boot-graalvm/target/native-image/spring-boot-graal started by jonashecht in /Users/jonashecht/dev/spring-boot/spring-boot-graalvm/target/native-image)\n2020-05-01 10:25:31.200  INFO 42231 --- [           main] i.j.s.SpringBootHelloApplication         : No active profile set, falling back to default profiles: default\n2020-05-01 10:25:31.241  WARN 42231 --- [           main] io.netty.channel.DefaultChannelId        : Failed to find the current process ID from ''; using a random value: 635087100\n2020-05-01 10:25:31.245  INFO 42231 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080\n2020-05-01 10:25:31.245  INFO 42231 --- [           main] i.j.s.SpringBootHelloApplication         : Started SpringBootHelloApplication in 0.078 seconds (JVM running for 0.08)\n```\n\nand uses only `30MB` of RAM:\n\n```\nProcesses: 501 total, 2 running, 499 sleeping, 2715 threads                                                                                                              10:26:05\nLoad Avg: 5.73, 10.11, 6.17  CPU usage: 4.33% user, 3.86% sys, 91.79% idle  SharedLibs: 162M resident, 34M data, 9248K linkedit.\nMemRegions: 214693 total, 2846M resident, 72M private, 1677M shared. PhysMem: 11G used (3607M wired), 4987M unused.\nVM: 2448G vsize, 1997M framework vsize, 77090986(192) swapins, 80042677(0) swapouts.  Networks: packets: 31169140/37G in, 27833716/33G out.\nDisks: 29775686/600G read, 11686485/480G written.\n\nPID    COMMAND      %CPU TIME     #TH  #WQ  #POR MEM  PURG CMPR PGRP  PPID STATE    BOOSTS    %CPU_ME %CPU_OTHRS UID  FAULT COW  MSGS MSGR SYSB SYSM CSW  PAGE IDLE POWE INST CYCL\n42231  spring-boot- 0.0  00:00.08 7    1    38   30M  0B   0B   42231 1592 sleeping *0[1]     0.00000 0.00000    501  17416 2360 77   20   2186 186  174  27   2    0.0  0    0\n```\n\nSo with a default Spring App we have around 500MB memory consumption, a natively compiled Spring App has only 30MB. That means, we could run more than 15 Spring microservices with the same amount of RAM we needed for only one standard Spring microservice! Wohoo! :) \n\nAnd not to mention the startup times. Around 1.5 seconds versus only 78 milli seconds. So even our Kubernetes cluster is able to scale our Spring Boot Apps at lightning speed!\n\n\n\n# Build and Run your Native Image compilation on a Cloud-CI provider like TravisCI\n\nAs we are used to test-driven development and we rely on very new code, which is for sure subject to change in the near future, we should be also able to automatically run our GraalVM Native image complilation on a Cloud CI provider like \n\nIn order to run the compilation process, we need to [install GraalVM and GraalVM Native Image first on TravisCI](https://stackoverflow.com/a/61254927/4964553). Therefore let's have a look into our [.travis.yml](.travis.yml):\n\n```yaml\ndist: bionic\nlanguage: minimal\n\ninstall:\n  # Install GraalVM with SDKMAN\n  - curl -s \"https://get.sdkman.io\" | bash\n  - source \"$HOME/.sdkman/bin/sdkman-init.sh\"\n  - sdk install java 20.2.0.r11-grl\n\n  # Check if GraalVM was installed successfully\n  - java -version\n\n  # Install Maven, that uses GraalVM for later builds\n  - sdk install maven\n\n  # Show Maven using GraalVM JDK\n  - mvn --version\n\n  # Install GraalVM Native Image\n  - gu install native-image\n\n  # Check if Native Image was installed properly\n  - native-image --version\n\nscript:\n  # Run GraalVM Native Image compilation of Spring Boot App\n  - ./compile.sh\n```\n\nThere are two main things to notice here: First we simply leverage the power of SDKMAN again to install GraalVM, as we already did on our local machines.\n\nSecond: __Don't use a `language: java` or the default linux distros like `dist: bionic`!__, because they ship with pre-installed Maven versions, which is configured to use the pre-installed OpenJDK - and __NOT our GraalVM installation__.\n\nTherefore we simply use the `language: minimal`, which is [a simple way of getting our Travis builds based on a basic Travis build environment without pre-installed JDKs or Maven](https://stackoverflow.com/a/44738181/4964553) together with `distro: bionic` which will tell Travis to use the latest available `minimal` build image (see https://docs.travis-ci.com/user/languages/minimal-and-generic/).\n\nNow our TravisCI builds should run a full native image compilation:\n\n```\nWarning: class initialization of class io.netty.handler.ssl.JettyNpnSslEngine failed with exception java.lang.NoClassDefFoundError: org/eclipse/jetty/npn/NextProtoNego$Provider. This class will be initialized at run time because option --allow-incomplete-classpath is used for image building. Use the option --initialize-at-run-time=io.netty.handler.ssl.JettyNpnSslEngine to explicitly request delayed initialization of this class.\n[spring-boot-graal:5634]   (typeflow): 238,622.47 ms,  6.23 GB\n[spring-boot-graal:5634]    (objects): 122,937.15 ms,  6.23 GB\n[spring-boot-graal:5634]   (features):  10,311.79 ms,  6.23 GB\n[spring-boot-graal:5634]     analysis: 379,203.23 ms,  6.23 GB\n[spring-boot-graal:5634]     (clinit):   2,542.77 ms,  6.23 GB\n[spring-boot-graal:5634]     universe:   9,890.85 ms,  6.23 GB\n[spring-boot-graal:5634]      (parse):  20,901.16 ms,  6.23 GB\n[spring-boot-graal:5634]     (inline):  14,131.55 ms,  6.23 GB\n[spring-boot-graal:5634]    (compile):  94,847.99 ms,  6.23 GB\n[spring-boot-graal:5634]      compile: 133,862.12 ms,  6.23 GB\n[spring-boot-graal:5634]        image:   8,635.21 ms,  6.23 GB\n[spring-boot-graal:5634]        write:   1,472.98 ms,  6.23 GB\n``` \n \nSee this build for example:\n\n![successfull-travis-compile](screenshots/successfull-travis-compile.png)\n\n### Tackling the 'There was an error linking the native image /usr/bin/ld: final link failed: Memory exhausted' error\n\nI now had Travis finally compiling my Spring Boot App - but with a last error (you can [see full log here](https://travis-ci.org/github/jonashackt/spring-boot-graalvm)):\n\n```\n[spring-boot-graal:5634]   (typeflow): 238,622.47 ms,  6.23 GB\n[spring-boot-graal:5634]    (objects): 122,937.15 ms,  6.23 GB\n[spring-boot-graal:5634]   (features):  10,311.79 ms,  6.23 GB\n[spring-boot-graal:5634]     analysis: 379,203.23 ms,  6.23 GB\n[spring-boot-graal:5634]     (clinit):   2,542.77 ms,  6.23 GB\n[spring-boot-graal:5634]     universe:   9,890.85 ms,  6.23 GB\n[spring-boot-graal:5634]      (parse):  20,901.16 ms,  6.23 GB\n[spring-boot-graal:5634]     (inline):  14,131.55 ms,  6.23 GB\n[spring-boot-graal:5634]    (compile):  94,847.99 ms,  6.23 GB\n[spring-boot-graal:5634]      compile: 133,862.12 ms,  6.23 GB\n[spring-boot-graal:5634]        image:   8,635.21 ms,  6.23 GB\n[spring-boot-graal:5634]        write:   1,472.98 ms,  6.23 GB\nFatal error: java.lang.RuntimeException: java.lang.RuntimeException: There was an error linking the native image: Linker command exited with 1\n\nLinker command executed:\ncc -v -o /home/travis/build/jonashackt/spring-boot-graalvm/target/native-image/spring-boot-graal -z noexecstack -Wl,--gc-sections -Wl,--dynamic-list -Wl,/tmp/SVM-8253584528623373425/exported_symbols.list -Wl,-x -L/tmp/SVM-8253584528623373425 -L/home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib -L/home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64 /tmp/SVM-8253584528623373425/spring-boot-graal.o /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libnet.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libjava.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libzip.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libnio.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libextnet.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/libffi.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/liblibchelper.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/libjvm.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/libstrictmath.a -lpthread -ldl -lz -lrt\n\nLinker command ouput:\nUsing built-in specs.\nCOLLECT_GCC=cc\nCOLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper\nOFFLOAD_TARGET_NAMES=nvptx-none\nOFFLOAD_TARGET_DEFAULT=1\nTarget: x86_64-linux-gnu\nConfigured with: ../src/configure -v --with-pkgversion='Ubuntu 7.4.0-1ubuntu1~18.04.1' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu\nThread model: posix\ngcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1) \nCOMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/\nLIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../:/lib/:/usr/lib/\nCOLLECT_GCC_OPTIONS='-v' '-o' '/home/travis/build/jonashackt/spring-boot-graalvm/target/native-image/spring-boot-graal' '-z' 'noexecstack' '-L/tmp/SVM-8253584528623373425' '-L/home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib' '-L/home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64' '-mtune=generic' '-march=x86-64'\n /usr/lib/gcc/x86_64-linux-gnu/7/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper -plugin-opt=-fresolution=/tmp/ccHdD8kF.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o /home/travis/build/jonashackt/spring-boot-graalvm/target/native-image/spring-boot-graal -z noexecstack /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o -L/tmp/SVM-8253584528623373425 -L/home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib -L/home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64 -L/usr/lib/gcc/x86_64-linux-gnu/7 -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. --gc-sections --dynamic-list /tmp/SVM-8253584528623373425/exported_symbols.list -x /tmp/SVM-8253584528623373425/spring-boot-graal.o /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libnet.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libjava.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libzip.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libnio.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libextnet.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/libffi.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/liblibchelper.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/libjvm.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/libstrictmath.a -lpthread -ldl -lz -lrt -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o\n/usr/bin/ld: final link failed: Memory exhausted\ncollect2: error: ld returned 1 exit status\n\n\tat java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)\n\tat java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)\n\tat java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)\n\tat java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:600)\n\tat java.base/java.util.concurrent.ForkJoinTask.get(ForkJoinTask.java:1006)\n\tat com.oracle.svm.hosted.NativeImageGenerator.run(NativeImageGenerator.java:462)\n\tat com.oracle.svm.hosted.NativeImageGeneratorRunner.buildImage(NativeImageGeneratorRunner.java:357)\n\tat com.oracle.svm.hosted.NativeImageGeneratorRunner.build(NativeImageGeneratorRunner.java:501)\n\tat com.oracle.svm.hosted.NativeImageGeneratorRunner.main(NativeImageGeneratorRunner.java:115)\n\tat com.oracle.svm.hosted.NativeImageGeneratorRunner$JDK9Plus.main(NativeImageGeneratorRunner.java:528)\nCaused by: java.lang.RuntimeException: There was an error linking the native image: Linker command exited with 1\n\nLinker command executed:\ncc -v -o /home/travis/build/jonashackt/spring-boot-graalvm/target/native-image/spring-boot-graal -z noexecstack -Wl,--gc-sections -Wl,--dynamic-list -Wl,/tmp/SVM-8253584528623373425/exported_symbols.list -Wl,-x -L/tmp/SVM-8253584528623373425 -L/home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib -L/home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64 /tmp/SVM-8253584528623373425/spring-boot-graal.o /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libnet.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libjava.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libzip.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libnio.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libextnet.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/libffi.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/liblibchelper.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/libjvm.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/libstrictmath.a -lpthread -ldl -lz -lrt\n\nLinker command ouput:\nUsing built-in specs.\nCOLLECT_GCC=cc\nCOLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper\nOFFLOAD_TARGET_NAMES=nvptx-none\nOFFLOAD_TARGET_DEFAULT=1\nTarget: x86_64-linux-gnu\nConfigured with: ../src/configure -v --with-pkgversion='Ubuntu 7.4.0-1ubuntu1~18.04.1' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu\nThread model: posix\ngcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1) \nCOMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/\nLIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../:/lib/:/usr/lib/\nCOLLECT_GCC_OPTIONS='-v' '-o' '/home/travis/build/jonashackt/spring-boot-graalvm/target/native-image/spring-boot-graal' '-z' 'noexecstack' '-L/tmp/SVM-8253584528623373425' '-L/home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib' '-L/home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64' '-mtune=generic' '-march=x86-64'\n /usr/lib/gcc/x86_64-linux-gnu/7/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper -plugin-opt=-fresolution=/tmp/ccHdD8kF.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o /home/travis/build/jonashackt/spring-boot-graalvm/target/native-image/spring-boot-graal -z noexecstack /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o -L/tmp/SVM-8253584528623373425 -L/home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib -L/home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64 -L/usr/lib/gcc/x86_64-linux-gnu/7 -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. --gc-sections --dynamic-list /tmp/SVM-8253584528623373425/exported_symbols.list -x /tmp/SVM-8253584528623373425/spring-boot-graal.o /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libnet.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libjava.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libzip.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libnio.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/libextnet.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/libffi.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/liblibchelper.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/libjvm.a /home/travis/.sdkman/candidates/java/20.0.0.r11-grl/lib/svm/clibraries/linux-amd64/libstrictmath.a -lpthread -ldl -lz -lrt -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o\n/usr/bin/ld: final link failed: Memory exhausted\ncollect2: error: ld returned 1 exit status\n\n\tat com.oracle.svm.hosted.image.NativeBootImageViaCC.handleLinkerFailure(NativeBootImageViaCC.java:424)\n\tat com.oracle.svm.hosted.image.NativeBootImageViaCC.write(NativeBootImageViaCC.java:399)\n\tat com.oracle.svm.hosted.NativeImageGenerator.doRun(NativeImageGenerator.java:657)\n\tat com.oracle.svm.hosted.NativeImageGenerator.lambda$run$0(NativeImageGenerator.java:445)\n\tat java.base/java.util.concurrent.ForkJoinTask$AdaptedRunnableAction.exec(ForkJoinTask.java:1407)\n\tat java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)\n\tat java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)\n\tat java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)\n\tat java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)\n\tat java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)\nError: Image build request failed with exit status 1\n\nreal\t9m11.937s\nuser\t17m46.032s\nsys\t0m11.720s\n```\n\n# Build and Run your Native Image compilation on GitHub Actions\n\nSince Travis laid down their OpenSource support to a massive degree, many maintainers move their repos over to GitHub Actions - see also this post: https://blog.codecentric.de/en/2021/02/github-actions-pipeline/\n\nSo let's implement a [.github/workflows/native-image-compile.yml](.github/workflows/native-image-compile.yml):\n\n```yaml\nname: native-image-compile\n\non: [push]\n\njobs:\n  native-image-compile-on-host:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n\n    - name: Cache SDKMAN archives \u0026 candidates\n      uses: actions/cache@v2\n      with:\n        path: ~/.sdkman\n        key: ${{ runner.os }}-sdkman-${{ hashFiles('pom.xml') }}\n        restore-keys: |\n          ${{ runner.os }}-sdkman-\n\n    - name: Install GraalVM, Maven, Native Image \u0026 Run Maven build\n      run: |\n        echo 'Install GraalVM with SDKMAN'\n        curl -s \"https://get.sdkman.io\" | bash\n        source \"$HOME/.sdkman/bin/sdkman-init.sh\"\n        sdk install java 20.2.0.r11-grl\n\n        echo 'Check if GraalVM was installed successfully'\n        java -version\n\n        echo 'Install GraalVM Native Image'\n        gu install native-image\n\n        echo 'Check if Native Image was installed properly'\n        native-image --version\n\n        echo 'Install Maven, that uses GraalVM for later builds'\n        source \"$HOME/.sdkman/bin/sdkman-init.sh\"\n        sdk install maven\n\n        echo 'Show Maven using GraalVM JDK'\n        mvn --version\n\n        echo 'Run GraalVM Native Image compilation of Spring Boot App (Maven version instead of ./compile.sh)'\n        mvn -B clean package -P native --no-transfer-progress\n```\n\nThis one does exactly what we did with TravisCI - building the native image using Maven and installing GraalVM beforehand.\n\n\n# Use Docker to compile a Spring Boot App with GraalVM\n\nThere's an [official Docker image from Oracle](https://github.com/orgs/graalvm/packages/container/package/graalvm-ce), but this one sadyl lacks both Maven with it's `mvn` command and the `native-image` plugin also not installed.\n\nBut we can help ourselves - we just craft a simple [Dockerfile](Dockerfile) for us. We're already used to leverage SDKMAN to install Maven. Therefore we need to install `unzip` and `zip` first, since SDKMAN needs both to work properly:\n\n```dockerfile\n# Simple Dockerfile adding Maven and GraalVM Native Image compiler to the standard\n# https://github.com/orgs/graalvm/packages/container/package/graalvm-ce image\nFROM ghcr.io/graalvm/graalvm-ce:ol7-java11-20.3.1.2\n\n# For SDKMAN to work we need unzip \u0026 zip\nRUN yum install -y unzip zip\n\nRUN \\\n    # Install SDKMAN\n    curl -s \"https://get.sdkman.io\" | bash; \\\n    source \"$HOME/.sdkman/bin/sdkman-init.sh\"; \\\n    sdk install maven; \\\n    # Install GraalVM Native Image\n    gu install native-image;\n\nRUN source \"$HOME/.sdkman/bin/sdkman-init.sh\" \u0026\u0026 mvn --version\n\nRUN native-image --version\n\n# Always use source sdkman-init.sh before any command, so that we will be able to use 'mvn' command\nENTRYPOINT bash -c \"source $HOME/.sdkman/bin/sdkman-init.sh \u0026\u0026 $0\"\n```\n\nIn order to enable the `mvn` command for a user of our Docker image, we craft a slightly more interesting `ENTRYPOINT` that always prefixes commands with `\"source $HOME/.sdkman/bin/sdkman-init.sh`.\n\nNow let's build our Image with:\n\n```shell script\ndocker build . --tag=graalvm-ce:20.3.0-java11-mvn-native-image\n```\n\nNow we should be able to launch our GraalVM Native Image compilation inside official Oracle GraalVM image with:\n\n```shell script\ndocker run -it --rm \\\n    --volume $(pwd):/build \\\n    --workdir /build \\\n    --volume \"$HOME\"/.m2:/root/.m2 \\\n    graalvm-ce:20.3.0-java11-mvn-native-image ./compile.sh\n```\n\nWhen I first thought about a Docker usage, I wanted to pack this build into a `Dockerfile` also - but then I realized, that there's [no easy way of using Docker volumes at Docker build time](https://stackoverflow.com/questions/51086724/docker-build-using-volumes-at-build-time). But I really wanted to mount a Docker volume to my local Maven repository like `--volume \"$HOME\"/.m2:/root/.m2` to prevent the download of all the Spring Maven dependencies over and over again every time we start our Docker container.\n\nSo I went with another way: We simply use a `docker run` command, that will compile our native Spring Boot app into our project's working directory (with `--volume $(pwd):/build`).\n\nThe resulting `spring-boot-graal` native App should be ready after some minutes of heavy compilation.\n\n__But!__ We're not able to run it! Hell yeah - because we turned our platform independend Java App into a platform dependend one! That's the price for speed I guess :)\n\n\n### Tackling 'Exception java.lang.OutOfMemoryError in thread \"native-image pid watcher\"' error\n\nSometimes the `docker run` seems to take ages to complete - and then a `java.lang.OutOfMemoryError` is thrown into the log:\n\n```\n14:06:34.609 [ForkJoinPool-2-worker-3] DEBUG io.netty.handler.codec.compression.ZlibCodecFactory - -Dio.netty.noJdkZlibEncoder: false\nException in thread \"native-image pid watcher\"\nException: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread \"native-image pid watcher\"\n```\n\nThen it is very likely that your Docker Engine has not enough RAM it is able to use! In my Mac installation the default is only `2.00 GB`:\n\n![docker-mac-memory](screenshots/docker-mac-memory.png)\n\nAs [stated in the comments of this so q\u0026a](https://stackoverflow.com/questions/57935533/native-image-building-process-is-frozen-in-quarkus), you have to give Docker much more memory since the GraalVM Native Image compilation process is really RAM intensive. I had a working local compilation in the Docker Container when I gave Docker `12.00 GB` of RAM.\n\n\n### Run Spring Boot Native Apps in Docker\n\nNow that our Docker build works in general, we should also run our Native Spring Boot App inside a Docker container. Therefore a Docker multi-stage build would come in handy, since we could then do the build \u0026 Native Image compilation stuff in the first container - and then only take the resulting Native app and use it in the second container to run it.\n\nTherefore let's refactor our Dockerfile:\n\n```dockerfile\n# Simple Dockerfile adding Maven and GraalVM Native Image compiler to the standard\n# https://github.com/orgs/graalvm/packages/container/package/graalvm-ce image\nFROM ghcr.io/graalvm/graalvm-ce:ol7-java11-20.3.1.2\n\nADD . /build\nWORKDIR /build\n\n# For SDKMAN to work we need unzip \u0026 zip\nRUN yum install -y unzip zip\n\nRUN \\\n    # Install SDKMAN\n    curl -s \"https://get.sdkman.io\" | bash; \\\n    source \"$HOME/.sdkman/bin/sdkman-init.sh\"; \\\n    sdk install maven; \\\n    # Install GraalVM Native Image\n    gu install native-image;\n\nRUN source \"$HOME/.sdkman/bin/sdkman-init.sh\" \u0026\u0026 mvn --version\n\nRUN native-image --version\n\nRUN source \"$HOME/.sdkman/bin/sdkman-init.sh\" \u0026\u0026 ./compile.sh\n\n\n# We use a Docker multi-stage build here in order that we only take the compiled native Spring Boot App from the first build container\nFROM oraclelinux:7-slim\n\nMAINTAINER Jonas Hecht\n\n# Add Spring Boot Native app spring-boot-graal to Container\nCOPY --from=0 \"/build/target/native-image/spring-boot-graal\" spring-boot-graal\n\n# Fire up our Spring Boot Native app by default\nCMD [ \"sh\", \"-c\", \"./spring-boot-graal\" ]\n```\n\nAdditionally the second container isn't based on the `ghcr.io/graalvm/graalvm-ce` image containing a GraalVM installation, Maven and the `native-image` command - but instead uses [the base image of this image](https://github.com/oracle/docker-images/blob/master/GraalVM/CE/Dockerfile.java11), which is `oraclelinux:7-slim`.\n\nWith that we reduce the resulting Docker image size from around `1.48GB` to only `186MB`!\n\nLet't run our Multi-stage build with the following command:\n\n\n```shell script\ndocker build . --tag=spring-boot-graal\n```\n\nThis again will take a while - you may grab a coffee :)\n\nAfter the Docker build successfully finished with some output like that:\n\n```\n[spring-boot-graal:289]   (typeflow): 114,554.33 ms,  6.58 GB\n[spring-boot-graal:289]    (objects):  63,145.07 ms,  6.58 GB\n[spring-boot-graal:289]   (features):   6,990.75 ms,  6.58 GB\n[spring-boot-graal:289]     analysis: 190,400.92 ms,  6.58 GB\n[spring-boot-graal:289]     (clinit):   1,970.98 ms,  6.67 GB\n[spring-boot-graal:289]     universe:   6,263.93 ms,  6.67 GB\n[spring-boot-graal:289]      (parse):  11,824.83 ms,  6.67 GB\n[spring-boot-graal:289]     (inline):   7,216.63 ms,  6.73 GB\n[spring-boot-graal:289]    (compile):  63,692.52 ms,  6.77 GB\n[spring-boot-graal:289]      compile:  86,836.76 ms,  6.77 GB\n[spring-boot-graal:289]        image:  10,050.63 ms,  6.77 GB\n[spring-boot-graal:289]        write:   1,319.52 ms,  6.77 GB\n[spring-boot-graal:289]      [total]: 313,644.65 ms,  6.77 GB\n\nreal\t5m16.447s\nuser\t16m32.096s\nsys\t1m34.441s\nRemoving intermediate container 151e1413ec2f\n ---\u003e be671d4f237f\nStep 10/13 : FROM docker pull ghcr.io/graalvm/graalvm-ce:ol7-java11-20.3.1.2\n ---\u003e 364d0bb387bd\nStep 11/13 : MAINTAINER Jonas Hecht\n ---\u003e Using cache\n ---\u003e 445833938b60\nStep 12/13 : COPY --from=0 \"/build/target/native-image/spring-boot-graal\" spring-boot-graal\n ---\u003e 2d717a0db703\nStep 13/13 : CMD [ \"sh\", \"-c\", \"./spring-boot-graal\" ]\n ---\u003e Running in 7fa931991d7e\nRemoving intermediate container 7fa931991d7e\n ---\u003e a0afe30b3619\nSuccessfully built a0afe30b3619\nSuccessfully tagged spring-boot-graal:latest\n```\n\nWe are able to run our Spring Boot Native app with `docker run -p 8080:8080 spring-boot-graal`: \n\n```\n$ docker run -p 8080:8080 spring-boot-graal\n\n  .   ____          _            __ _ _\n /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\\n( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\\n \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )\n  '  |____| .__|_| |_|_| |_\\__, | / / / /\n =========|_|==============|___/=/_/_/_/\n :: Spring Boot ::\n\n2020-04-19 09:22:51.547  INFO 1 --- [           main] i.j.s.SpringBootHelloApplication         : Starting SpringBootHelloApplication on 06274db526b0 with PID 1 (/spring-boot-graal started by root in /)\n2020-04-19 09:22:51.547  INFO 1 --- [           main] i.j.s.SpringBootHelloApplication         : No active profile set, falling back to default profiles: default\n2020-04-19 09:22:51.591  WARN 1 --- [           main] io.netty.channel.DefaultChannelId        : Failed to find the current process ID from ''; using a random value: -949685832\n2020-04-19 09:22:51.593  INFO 1 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080\n2020-04-19 09:22:51.594  INFO 1 --- [           main] i.j.s.SpringBootHelloApplication         : Started SpringBootHelloApplication in 0.063 seconds (JVM running for 0.065)\n```\n\nNow simply access your App via http://localhost:8080/hello\n\n\n# Running Spring Boot Graal Native Apps on Heroku\n\nFinally we are where we wanted to be in the first place! We're able to run our natively compiled Spring Boot Apps inside Docker containers. It should be easy to deploy those to [a cloud provider like Heroku](https://heroku.com)!\n\nAnd it's good to get back on my last year's article on Running [Spring Boot on Heroku with Docker, JDK 11 \u0026 Maven 3.5.x](https://blog.codecentric.de/en/2019/08/spring-boot-heroku-docker-jdk11/), since there may be tweaks we need with our Graal-Setup also!\n\nNow as we move forward to a deployment of our Spring Boot Native app on a cloud provider's Docker infrastructure, we need to have our Spring Boot Native app's port configurable in a dynamic fashion! Most cloud providers want to dynamically set this port from the outside - as [we can see in Heroku for example](https://devcenter.heroku.com/articles/setting-the-http-port-for-java-applications).\n\n[As the Heroku docs state]( https://devcenter.heroku.com/articles/container-registry-and-runtime#dockerfile-commands-and-runtime):\n                                                                                                                    \n\u003e The web process must listen for HTTP traffic on $PORT, which is set by Heroku. EXPOSE in Dockerfile is not respected, but can be used for local testing. Only HTTP requests are supported.\n\n\n### Configure the Spring Boot Native app's port dynamically inside a Docker container\n\nTo achieve that, we need to somehow pass a port variable to our Spring Boot Native app from command line. Since the GraalVM support is just in its early stages, we can't rely on a huge documentation. But as this is a similar problem other frameworks also needed to solve, I thought about [Quarkus.io](https://quarkus.io/) which has been around for some time now - and should have had exactly this problem already.\n\nAnd [there's the stackoverflow answer](https://stackoverflow.com/a/55043637/4964553) :) With Quarkus, you simply need to pass the port as `-D` parameter like `-Dquarkus.http.port=8081` to the native app.\n\n[Could this be mapped onto Spring Boot too?](https://stackoverflow.com/questions/61302412/how-to-configure-the-port-of-a-spring-boot-app-thats-natively-compiled-by-graal) Luckily yes! Just run your Spring Boot native app with\n\n```shell script\n./spring-boot-graal -Dserver.port=8087\n```\n\nAnd your App starts using port `8087` :)\n\nNow we are able to pass the port dynamically from a `docker run` command. Therefore we need to make a small change to our [Dockerfile](Dockerfile):\n\n```dockerfile\n...\n# Add Spring Boot Native app spring-boot-graal to Container\nCOPY --from=0 \"/build/target/native-image/spring-boot-graal\" spring-boot-graal\n\n# Fire up our Spring Boot Native app by default\nCMD [ \"sh\", \"-c\", \"./spring-boot-graal -Dserver.port=$PORT\" ]\n```\n\nWith this we are able to run our Dockerized Spring Boot Native App with a dynamic port setting from command line like this:\n\n```\ndocker run -e \"PORT=8087\" -p 8087:8087 spring-boot-graal\n```\n\nFinally try to access your app at http://localhost:8087/hello\n\n\n### Use Docker to run our Spring Boot Native App on Heroku\n\nFirst things first: Let's start by creating your Heroku app if you haven't already:\n\n```\nheroku create spring-boot-graal\n```\n\nThen you simply set the Heroku stack:\n\n```\nheroku stack:set container --app spring-boot-graal\n```\n\nSadly we can't use the section __'Configuring Heroku to use Docker'__ of my article on Running [Spring Boot on Heroku with Docker, JDK 11 \u0026 Maven 3.5.x](https://blog.codecentric.de/en/2019/08/spring-boot-heroku-docker-jdk11/) in this case here, since we would run into the `Error: Image build request failed with exit status 137`.\n                                                                                                                                                                                                                                                           \nMy first attempts on Heroku lead to the build problems:\n\n```\nError: Image build request failed with exit status 137\nreal\t2m51.946s\nuser\t2m9.594s\nsys\t0m19.085s\nThe command '/bin/sh -c source \"$HOME/.sdkman/bin/sdkman-init.sh\" \u0026\u0026 ./compile.sh' returned a non-zero code: 137\n```\n\nThis error appears usually [when Docker does not have enough memory](https://codefresh.io/docs/docs/troubleshooting/common-issues/error-code-137/). And since the free Heroku dyno only guarantees us `512MB` of RAM :( ([see Dyno Types](https://devcenter.heroku.com/articles/dyno-types))), we won't get far on this way.\n\nBut [as the docs state](https://devcenter.heroku.com/categories/deploying-with-docker) the way of [Building Docker Images with heroku.yml](https://devcenter.heroku.com/articles/build-docker-images-heroku-yml) isn't the only way to run Docker containers on Heroku. There's another way of using the [Container Registry \u0026 Runtime (Docker Deploys)](https://devcenter.heroku.com/articles/container-registry-and-runtime)!\n\nWith that we could decouple the Docker image build process (which is so much memory hungry!) from simply running the Docker container based on that image.\n\n\n### Work around the Heroku 512MB RAM cap: Building our Dockerimage with TravisCI\n\nSo we need to do the Docker build on another platform - why not simply use Travis?! It already proofed to work directly on the host, why not also [using the Travis Docker service](https://docs.travis-ci.com/user/docker/)?!\n\nLeveraging [Travis jobs feature](https://docs.travis-ci.com/user/build-stages/), we can also do both in parallel - just have a look at the following screenshot:\n\n![travis-parallel-jobs-direct-and-docker](screenshots/travis-parallel-jobs-direct-and-docker.png)\n\n\nTherefore we implement two separate Travis jobs `\"Native Image compile on Travis Host\"` and `\"Native Image compile in Docker on Travis \u0026 Push to Heroku Container Registry\"` inside our [.travis.yml](.travis.yml) and include the `docker` services:\n\n```yaml\n# use minimal Travis build image so that we could install our own JDK (Graal) and Maven\n# use newest available minimal distro - see https://docs.travis-ci.com/user/languages/minimal-and-generic/\ndist: bionic\nlanguage: minimal\n\nservices:\n  - docker\n\njobs:\n  include:\n    - script:\n        # Install GraalVM with SDKMAN\n        - curl -s \"https://get.sdkman.io\" | bash\n        - source \"$HOME/.sdkman/bin/sdkman-init.sh\"\n        - sdk install java 20.2.0.r11-grl\n\n        # Check if GraalVM was installed successfully\n        - java -version\n\n        # Install Maven, that uses GraalVM for later builds\n        - sdk install maven\n\n        # Show Maven using GraalVM JDK\n        - mvn --version\n\n        # Install GraalVM Native Image\n        - gu install native-image\n\n        # Check if Native Image was installed properly\n        - native-image --version\n\n        # Run GraalVM Native Image compilation of Spring Boot App\n        - ./compile.sh\n\n      name: \"Native Image compile on Travis Host\"\n\n    - script:\n        # Compile with Docker\n        - docker build . --tag=spring-boot-graal\n      name: \"Native Image compile in Docker on Travis \u0026 Push to Heroku Container Registry\"\n```\n\n### Tackling 'Error: Image build request failed with exit status 137' with the -J-Xmx parameter\n\n[As mentioned in the Spring docs](https://repo.spring.io/milestone/org/springframework/experimental/spring-graalvm-native-docs/0.7.0/spring-graalvm-native-docs-0.7.0.zip!/reference/index.html#_options_enabled_by_default), the `spring-graalvm-native` uses the `--no-server` option by default when running Native Image compilations with Spring.\n\nBut why is this parameter used? See the official docs: https://www.graalvm.org/docs/reference-manual/native-image/\n\n\u003e Another prerequisite to consider is the maximum heap size. Physical memory for running a JVM-based application may be insufficient to build a native image. For server-based image building we allow to use 80% of the reported physical RAM for all servers together, but never more than 14GB per server (for exact details please consult the native-image source code). If you run with --no-server option, you will get the whole 80% of what is reported as physical RAM as the baseline. This mode respects -Xmx arguments additionally.\n\nWe somehow could leave out the `no-server` option in order to reduce the amount of memory our Native Image compilation consumes - but there's an open issue in combination with Spring: https://github.com/oracle/graal/issues/1952 which says, that the images build without `--no-server` is sometimes unreliable.\n\nLuckily there's [a hint in this GitHub issue](https://github.com/oracle/graal/issues/920), that we could configure the amount of memory the `--no-server` option takes in total with the help of a `Xmx` parameter like `-J-Xmx3G`.\n\nUsing that option together like this in our `native-image` command:\n\n```shell script\ntime native-image \\\n  -J-Xmx4G \\\n  -H:+TraceClassInitialization \\\n  -H:Name=$ARTIFACT \\\n  -H:+ReportExceptionStackTraces \\\n  -Dspring.graal.remove-unused-autoconfig=true \\\n  -Dspring.graal.remove-yaml-support=true \\\n  -cp $CP $MAINCLASS;\n```\n\nwe could repeatably reduce the amount of memory to 4GBs of RAM, which should be enough for TravisCI - since it provides us with more than 6GB using the Docker service ([see this build for example](https://travis-ci.org/github/jonashackt/spring-boot-graalvm/builds/677157831)). Using the option results in the following output:\n\n```\n08:07:23.999 [ForkJoinPool-2-worker-3] DEBUG io.netty.util.internal.PlatformDependent - maxDirectMemory: 4294967296 bytes (maybe)\n...\n[spring-boot-graal:215]   (typeflow): 158,492.53 ms,  4.00 GB\n[spring-boot-graal:215]    (objects):  94,986.72 ms,  4.00 GB\n[spring-boot-graal:215]   (features): 104,518.36 ms,  4.00 GB\n[spring-boot-graal:215]     analysis: 368,005.35 ms,  4.00 GB\n[spring-boot-graal:215]     (clinit):   3,107.18 ms,  4.00 GB\n[spring-boot-graal:215]     universe:  12,502.04 ms,  4.00 GB\n[spring-boot-graal:215]      (parse):  22,617.13 ms,  4.00 GB\n[spring-boot-graal:215]     (inline):  10,093.57 ms,  3.49 GB\n[spring-boot-graal:215]    (compile):  82,256.99 ms,  3.59 GB\n[spring-boot-graal:215]      compile: 119,502.78 ms,  3.59 GB\n[spring-boot-graal:215]        image:  12,087.80 ms,  3.59 GB\n[spring-boot-graal:215]        write:   3,573.06 ms,  3.59 GB\n[spring-boot-graal:215]      [total]: 558,194.13 ms,  3.59 GB\n\nreal\t9m22.984s\nuser\t24m41.948s\nsys\t2m3.179s\n```\n\nThe one thing to take into account is that Native Image compilation will be a bit slower now. So if you run on your local machine with lot's of memory, feel free to delete the ` -J-Xmx4G` parameter :)\n\n\n### Work around the Heroku 512MB RAM cap: Building our Dockerimage with GitHub Actions\n\n```yaml\n  native-image-compile-in-docker:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v2\n\n      - name: Compile Native Image using Docker\n        run: docker build . --tag=registry.heroku.com/spring-boot-graal/web\n```\n\n\n### Pushing and Releasing our Dockerized Native Spring Boot App on Heroku Container Infrastructure\n\nNow we should be able to finally [push the build Docker image into Heroku's Container Registry](https://devcenter.heroku.com/articles/container-registry-and-runtime#using-a-ci-cd-platform), from where we're able to run our Spring Boot Native app later on.\n\nTherefore we need to [configure some environment variables in Travis in order to push](https://docs.travis-ci.com/user/docker/#pushing-a-docker-image-to-a-registry) to Heroku's Container Registry inside our TravisCI job's settings: `DOCKER_USERNAME` and `DOCKER_PASSWORD`. The first is your Heroku eMail, the latter is your Heroku API key. Be sure to prevent displaying the values in the build log:\n\n![travis-env-vars-heroku](screenshots/travis-env-vars-heroku.png)\n\nWith the following configuration inside our [.travis.yml](.travis.yml), we should be able to successfully log in to Heroku Container Registry:\n\n```yaml\n    - script:\n        # Login into Heroku Container Registry first, so that we can push our Image later\n        - echo \"$DOCKER_PASSWORD\" | docker login -u \"$DOCKER_USERNAME\" --password-stdin registry.heroku.com\n```\n\nNow after a successful Docker build, that compiles our Spring Boot App into a native executable, we finally need to push the resulting Docker image into Heroku Container Registry.\n\nTherefore we need to use the correct tag for our Docker image build([see the docs](https://devcenter.heroku.com/articles/container-registry-and-runtime#pushing-an-existing-image):\n\n```shell script\ndocker build . --tag=registry.heroku.com/\u003capp\u003e/\u003cprocess-type\u003e\ndocker push registry.heroku.com/\u003capp\u003e/\u003cprocess-type\u003e\n```\n\nThis means we add the following `docker tag` and `docker push` command into our [.travis.yml](.travis.yml):\n\n```yaml\n    - docker build . --tag=registry.heroku.com/spring-boot-graal/web\n    - docker push registry.heroku.com/spring-boot-graal/web\n```\n\n\nThe final step after a successful push is [to release our App on Heroku](https://devcenter.heroku.com/articles/container-registry-and-runtime#releasing-an-image), which is always the last step to deploy our App on Heroku using Docker [since May 2018](https://devcenter.heroku.com/changelog-items/1426) (before a push was all you had to do).\n\nThere are [two ways to achieve this](https://devcenter.heroku.com/articles/container-registry-and-runtime#releasing-an-image): either through the CLI via `heroku container:release web` or with the API. The first would require us to install Heroku CLI in Travis, the latter should work out-of-the-box. Therefore let's craft the needed `curl` command:\n\n```shell script\ncurl -X PATCH https://api.heroku.com/apps/spring-boot-graal/formation \\\n          -d '{\n                \"updates\": [\n                {\n                  \"type\": \"web\",\n                  \"docker_image\": \"'\"$(docker inspect registry.heroku.com/spring-boot-graal/web --format={{.Id}})\"'\"\n                }]\n              }' \\\n          -H \"Content-Type: application/json\" \\\n          -H \"Accept: application/vnd.heroku+json; version=3.docker-releases\" \\\n          -H \"Authorization: Bearer $DOCKER_PASSWORD\"\n```\n\nThis `curl` command is even better then the documented on in [the official Heroku docs](https://devcenter.heroku.com/articles/container-registry-and-runtime#api), since it already incorporates the `docker inspect registry.heroku.com/spring-boot-graal/web --format={{.Id}})` command to retrieve the needed Docker image id and also omits the need to login to Heroku CLI beforehand (to create the needed `~/.netrc` mentioned in the docs), since we simply use `-H \"Authorization: Bearer $DOCKER_PASSWORD\"` here, where `$DOCKER_PASSWORD` is our Heroku API Key again.\n\nThe problem with Travis: [It does not understand our nice curl](https://travis-ci.org/github/jonashackt/spring-boot-graalvm/jobs/679008339) command, [since it interprets it totally wrong](https://stackoverflow.com/questions/34687610/how-to-properly-use-curl-in-travis-ci-config-file-yaml), even if we mind [the correct multiline usage](https://travis-ci.community/t/yaml-multiline-strings/3914/4). Well I guess our Java User Group Thüringen speaker Kai Tödter did already know that restriction of some CI systems, and [crafted himself a bash script](https://toedter.com/2018/06/02/heroku-docker-deployment-update/) for exactly that purpose.\n\nAt that point I created a script called [heroku-release.sh](heroku-release.sh):\n\n```shell script\n#!/usr/bin/env bash\n\nherokuAppName=$1\ndockerImageId=$(docker inspect registry.heroku.com/$herokuAppName/web --format={{.Id}})\n\ncurl -X PATCH https://api.heroku.com/apps/$herokuAppName/formation \\\n          -d '{\n                \"updates\": [\n                {\n                  \"type\": \"web\",\n                  \"docker_image\": \"'\"$dockerImageId\"'\"\n                }]\n              }' \\\n          -H \"Content-Type: application/json\" \\\n          -H \"Accept: application/vnd.heroku+json; version=3.docker-releases\" \\\n          -H \"Authorization: Bearer $DOCKER_PASSWORD\"\n```\n\nUsing this script, we finally have our fully working [.travis.yml](.travis.yml):\n\n```yaml\ndist: bionic\nlanguage: minimal\n\nservices:\n  - docker\n\n- script:\n    # Login into Heroku Container Registry first, so that we can push our Image later\n    - echo \"$DOCKER_PASSWORD\" | docker login -u \"$DOCKER_USERNAME\" --password-stdin registry.heroku.com\n\n    # Compile App with Docker\n    - docker build . --tag=registry.heroku.com/spring-boot-graal/web\n\n    # Push to Heroku Container Registry\n    - docker push registry.heroku.com/spring-boot-graal/web\n\n    # Release Dockerized Native Spring Boot App on Heroku\n    - ./heroku-release.sh spring-boot-graal\n```\n\nThat's it! After a successfull TravisCI build, we should be able to see our running Dockerized Spring Boot Native App on Heroku at https://spring-boot-graal.herokuapp.com/hello\n\n![heroku-running-app](screenshots/heroku-running-app.png)\n\nYou can even use `heroku logs` to see what's happening behind the scenes:\n\n```\n$ heroku logs -a spring-boot-graal\n\n2020-04-24T12:02:14.562471+00:00 heroku[web.1]: State changed from down to starting\n2020-04-24T12:02:41.564599+00:00 heroku[web.1]: State changed from starting to up\n2020-04-24T12:02:41.283549+00:00 app[web.1]:\n2020-04-24T12:02:41.283574+00:00 app[web.1]: .   ____          _            __ _ _\n2020-04-24T12:02:41.283575+00:00 app[web.1]: /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\\n2020-04-24T12:02:41.283575+00:00 app[web.1]: ( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\\n2020-04-24T12:02:41.283576+00:00 app[web.1]: \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )\n2020-04-24T12:02:41.283576+00:00 app[web.1]: '  |____| .__|_| |_|_| |_\\__, | / / / /\n2020-04-24T12:02:41.283578+00:00 app[web.1]: =========|_|==============|___/=/_/_/_/\n2020-04-24T12:02:41.286498+00:00 app[web.1]: :: Spring Boot ::\n2020-04-24T12:02:41.286499+00:00 app[web.1]:\n2020-04-24T12:02:41.287774+00:00 app[web.1]: 2020-04-24 12:02:41.287  INFO 3 --- [           main] i.j.s.SpringBootHelloApplication         : Starting SpringBootHelloApplication on 1c7f1944-1f01-4284-8931-bc1a0a2d1fa5 with PID 3 (/spring-boot-graal started by u11658 in /)\n2020-04-24T12:02:41.287859+00:00 app[web.1]: 2020-04-24 12:02:41.287  INFO 3 --- [           main] i.j.s.SpringBootHelloApplication         : No active profile set, falling back to default profiles: default\n2020-04-24T12:02:41.425964+00:00 app[web.1]: 2020-04-24 12:02:41.425  WARN 3 --- [           main] io.netty.channel.DefaultChannelId        : Failed to find the current process ID from ''; using a random value: -36892848\n2020-04-24T12:02:41.427326+00:00 app[web.1]: 2020-04-24 12:02:41.427  INFO 3 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 59884\n2020-04-24T12:02:41.430874+00:00 app[web.1]: 2020-04-24 12:02:41.430  INFO 3 --- [           main] i.j.s.SpringBootHelloApplication         : Started SpringBootHelloApplication in 0.156 seconds (JVM running for 0.159)\n```\n\n\n### Pushing and Releasing our Dockerized Native Spring Boot App on Heroku Container Infrastructure using GitHub Actions\n\nWe should also use GitHub Actions to [push the build Docker image into Heroku's Container Registry](https://devcenter.heroku.com/articles/container-registry-and-runtime#using-a-ci-cd-platform).\n\nTherefore we need to configure encrypted variables in our GitHub repository in order to push to Heroku's Container Registry:\n`DOCKER_USERNAME` and `DOCKER_PASSWORD`. The first is your Heroku eMail, the latter is your Heroku API key. Be sure to prevent displaying the values in the build log:\n\nWith the following configuration inside our [.github/workflows/native-image-compile.yml](.github/workflows/native-image-compile.yml), we should be able to successfully log in to Heroku Container Registry:\n\n```yaml\n        run: |\n          echo ' Login into Heroku Container Registry first, so that we can push our Image later'\n          echo \"$DOCKER_PASSWORD\" | docker login -u \"$DOCKER_USERNAME\" --password-stdin registry.heroku.com\n```\n\nNow after a successful Docker build, that compiles our Spring Boot App into a native executable, we finally need to push the resulting Docker image into Heroku Container Registry.\n\nTherefore we need to use the correct tag for our Docker image build([see the docs](https://devcenter.heroku.com/articles/container-registry-and-runtime#pushing-an-existing-image):\n\n```shell script\ndocker build . --tag=registry.heroku.com/\u003capp\u003e/\u003cprocess-type\u003e\ndocker push registry.heroku.com/\u003capp\u003e/\u003cprocess-type\u003e\n```\n\nThis means we add the following `docker tag` and `docker push` command into our [.github/workflows/native-image-compile.yml](.github/workflows/native-image-compile.yml):\n\n```yaml\n          echo 'Compile Native Image using Docker'\n          docker build . --tag=registry.heroku.com/spring-boot-graal/web\n\n          echo 'Push to Heroku Container Registry'\n          docker push registry.heroku.com/spring-boot-graal/web\n```\n\nSee the paragraph on how to release to Heroku using Containers at [Pushing and Releasing our Dockerized Native Spring Boot App on Heroku Container Infrastructure](#pushing-and-releasing-our-dockerized-native-spring-boot-app-on-heroku-container-infrastructure).)\n\n\n\n# Autorelease on Docker Hub with TravisCI \u0026 GitHub Actions\n\nWe could try to __autorelease to Docker Hub on hub.docker.com:__ \n\nTherefore head over to the repositories tab in Docker Hub and click `Create Repository`:\n\n![docker-hub-create-repo](screenshots/docker-hub-create-repo.png)\n\nAs the docs state, there are some config options to [setup automated builds](https://docs.docker.com/docker-hub/builds/).\n\n__BUT:__ As the automatic builds feature rely on the Docker Hub build infrastructure, there woun't be enough RAM for our builds to succeed! You may try it, but you'll see those errors at the end:\n\n```\n13:13:26.080 [ForkJoinPool-2-worker-3] DEBUG io.netty.handler.codec.compression.ZlibCodecFactory - -Dio.netty.noJdkZlibEncoder: false\n#\n# There is insufficient memory for the Java Runtime Environment to continue.\n# Native memory allocation (mmap) failed to map 578920448 bytes for committing reserved memory.\n# An error report file with more information is saved as:\n# /build/target/native-image/hs_err_pid258.log\n\u001b[91mOpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x000000078d96d000, 578920448, 0) failed; error='Not enough space' (errno=12)\n\u001b[0m\n\u001b[91mError: Image build request failed with exit status 1\u001b[0m\n```\n\nSince our TravisCI \u0026 GitHub Actions builds are now enabled to successfully run our GraalVM Native Image compilation in a Docker build, we could live without the automatic builds feature of Docker Hub - and simply push our build image to Docker Hub also!\n\nTherefore you need to create an Access Token in your Docker Hub account at https://hub.docker.com/settings/security\n\nThen head over to your TravisCI \u0026 GitHub Actions project settings and add the environment variables `DOCKER_HUB_TOKEN` and `DOCKER_HUB_USERNAME` as already happended for Heroku Container Registry.\n\nThe final step then is to add the correct `docker login` and `docker push` commands to our [.travis.yml](.travis.yml) and [.github/workflows/native-image-compile.yml](.github/workflows/native-image-compile.yml):\n\n```yaml\n        # Push to Docker Hub also, since automatic Builds there don't have anough RAM to do a docker build\n        - echo \"$DOCKER_HUB_TOKEN\" | docker login -u \"$DOCKER_HUB_USERNAME\" --password-stdin\n        - docker tag registry.heroku.com/spring-boot-graal/web jonashackt/spring-boot-graalvm:latest\n        - docker push jonashackt/spring-boot-graalvm:latest\n```\n\nBe sure to also tag your image correctly according to your created Docker Hub repository.\n\nFinally, we should see our Docker images released on https://hub.docker.com/r/jonashackt/spring-boot-graalvm and could run this app simply by executing:\n\n```\ndocker run -e \"PORT=8087\" -p 8087:8087 jonashackt/spring-boot-graalvm:latest\n```\n\nThis pulls the latest `jonashackt/spring-boot-graalvm` image and runs our app locally.\n\n\n\n\n\n\n# Upgrade to spring-native (from spring-graalvm-native) \u0026 spring-aot-maven-plugin \u0026 GraalVM 21.3\n\nCurrent docs: https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/index.html#overview\n\nhttps://spring.io/blog/2021/03/11/announcing-spring-native-beta\n\n\n### spring-graalvm-native -\u003e spring-native\n\nSwitch from `spring-graalvm-native` to `spring-native`:\n\n```xml\n\u003cspring-graalvm-native.version\u003e0.8.5\u003c/spring-graalvm-native.version\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.springframework.experimental\u003c/groupId\u003e\n    \u003cartifactId\u003espring-graalvm-native\u003c/artifactId\u003e\n    \u003cversion\u003e${spring-graalvm-native.version}\u003c/version\u003e\n\u003c/dependency\u003e\n\nto\n\n\u003cspring-native.version\u003e0.10.5\u003c/spring-native.version\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.springframework.experimental\u003c/groupId\u003e\n    \u003cartifactId\u003espring-native\u003c/artifactId\u003e\n    \u003cversion\u003e${spring-native.version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n\n### Spring Boot Version \u003c=\u003e spring-native Version \u003c=\u003e GraalVM v","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonashackt%2Fspring-boot-graalvm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonashackt%2Fspring-boot-graalvm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonashackt%2Fspring-boot-graalvm/lists"}