How to deploy a (native) Quarkus Application on Heroku
Quarkus is a „container first“ framework for microservice development in Java (or alternatively Kotlin or Scala). With help of GraalVM Native Image, Quarkus applications can be compiled to native executables that run directly on the target OS, without JVM, with low memory footprint and blazing fast startup time.
Heroku is a Cloud Application Platform for building and hosting applications, while various languages can be used for development. It also allows to deploy and run self-built docker images.
Following the instructions from this blog post, we’ll create a native Quarkus application and deploy it in a docker container to the Heroku Runtime.
Prerequisites
The following needs to be installed on our development machine:
- Java 8 or 11(+)
- Maven
- Docker
- Heroku CLI
Creating the Quarkus app
(For more detailed instructions please consult the Quarkus website.)
We can bootstrap a quarkus project using the quarkus-maven-plugin
:
$ mvn io.quarkus:quarkus-maven-plugin:1.0.1.Final:create \ -DprojectGroupId=org.acme \ -DprojectArtifactId=hello-quarkus \ -DclassName="org.acme.quickstart.GreetingResource" \ -Dpath="/hello"
We change into the newly created project directory, and run the application in Quarkus Development Mode:
$ cd hello-quarkus $ mvn quarkus:dev
Now we can test the application in another terminal. The application already contains a „hello
“ REST resource. Request it with curl
(or open http://localhost:8080/hello in a browser):
$ curl localhost:8080/hello hello
Stop the Quarkus Dev Mode with Ctrl+C
.
Next, we’ll build the application to a native executable:
$ mvn package -Pnative -Dquarkus.native.container-build=true
With -Pnative
we activate a maven profile which creates the native executable. The parameter -Dquarkus.native.container-build=true
tells Quarkus to build the application within a docker container. This way we don’t need to have GraalVM installed on our machine, since it’s present in the docker container used for the build. The resulting application will be a Linux executable (even if it’s built on Windows).
Now we go and grab a coffee, since the build will take a while (about 3 minutes on my laptop). The result is a file in the target
directory named hello-quarkus-1.0-SNAPSHOT-runner
. On Linux or WSL (Windows Subsystem for Linux) you can run it like this:
$ ./target/hello-quarkus-1.0-SNAPSHOT-runner
Now we build a docker image containing this application. The Quarkus app contains a dockerfile to do this, src/main/docker/Dockerfile.native
(it’s content without comments is shown below):
FROM registry.access.redhat.com/ubi8/ubi-minimal WORKDIR /work/ COPY target/*-runner /work/application RUN chmod 775 /work EXPOSE 8080 CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
However we cannot use this out of the box, because Heroku requires that the application’s port can be set with the $PORT
environment variable. Therefore we need to change the last line to
CMD ./application -Dquarkus.http.host=0.0.0.0 -Dquarkus.http.port=${PORT}
. We can also get rid of the line above (EXPOSE 8080
), since we don’t really need it. The resulting Dockerfile.native
looks like this:
FROM registry.access.redhat.com/ubi8/ubi-minimal WORKDIR /work/ COPY target/*-runner /work/application RUN chmod 775 /work CMD ./application -Dquarkus.http.host=0.0.0.0 -Dquarkus.http.port=${PORT}
Using this we build a docker image named quarkus/hello-quarkus
…
$ docker build -f src/main/docker/Dockerfile.native -t quarkus/hello-quarkus .
… and run with docker:
$ docker run -i --rm --name hello_quarkus --env PORT=8081 -p 8081:8081 quarkus/hello-quarkus
Note how we specify the application’s port via the --env PORT=8081
parameter. Now we can request the „hello
“ resource again, this time using port 8081:
$ curl localhost:8081/hello hello
Deploying the app to Heroku
(The official documentation for this can be found on the Heroku website)
First we log in to our Heroku account:
$ heroku login
Next, we create a Heroku application (replace <app-name>
by whatever you want; however it must be unique on Heroku. Leave out the --region eu
part if you’re on the American continent; in this case the default region us
will be used):
$ heroku create <app-name> --region eu
Log in to the Heroku container registry:
$ heroku container:login
We tag the previously built docker image and push it to the Heroku container registry (replacing <app-name>
by the name of our Heroku app again):
$ docker tag quarkus/hello-quarkus registry.heroku.com/<app-name>/web $ docker push registry.heroku.com/<app-name>/web
Finally, we instruct Heroku to run the image like so (don’t forget to replace <app-name>
):
$ heroku container:release web -a <app-name>
Done! Now we can request the „hello
“ resource from our app running on Heroku (replacing <app-name>
again, you’ll guess):
$ curl https://<app-name>.herokuapp.com/hello hello
Conclusion
It’s not too hard to deploy a Quarkus app to Heroku. Most of the time we could just follow the instructions from the official documentation. The only thing we needed to do was adjust the Dockerfile, to meet Heroku’s requirement that the application’s port can be set via the $PORT
environment variable.