Build Hilla app as native image for deployment
A Hilla app that was created using the Hilla CLI, can be build as a native image for deployment using Maven (or Gradle). This requires a JDK with GraalVM support. A Hilla app built as a native image is a stand-alone executable that can be executed without a Java runtime environment. Compared to a Hilla app built and executed as a JAR file, a Hilla app built and executed as a native image is usually characterized by a lower startup time and memory usage. A Hilla app can be built as a native image with the support of GraalVM as follows:
./mvnw clean package -Pproduction -Pnative native:compile
The native image created contains all the necessary classes, dependencies and the frontend bundle. As a Hilla app is a classic Spring Boot project, the native image created also contains an integrated server that serves the Hilla app as soon as the native image is executed.
Further information on Spring Boot’s support for native images can be found here.
Execute Hilla app as native image
The built Hilla app my-app
can be executed as a native image as follows:
./target/my-app
Spring Boot specific configurations can be set or overridden as environment variables if required. For example, it is possible to adjust the log level at the start of the application as follows:
export LOGGING_LEVEL_COM_VAADIN=DEBUG && ./target/my-app
Create Docker image for Hilla app
Applications are often deployed based on containers. It is therefore advisable to create a Dockerfile
for the Hilla app. The Dockerfile
should be located in the base directory of the Hilla app and have the following content:
FROM debian:bookworm-slim
WORKDIR /app
COPY target/*.so /app/
COPY target/my-app /app/my-app
EXPOSE 8080
CMD ["/app/my-app"]
The Dockerfile
is based on a slimmed-down base image, here debian:bookworm-slim
. The native image of the Hilla app is copied into the Docker image together with the libraries (.so
files) created during the build. The libraries provide dependencies that are not part of the native image but are required by the application during execution. Furthermore, the Dockerfile
contains the configuration for the port via which the application can receive requests. The CMD
is used to specify that the native image in the Docker image should be executed when the container is started.
Create Docker image locally
The Docker image can be created locally with Docker or Podman:
docker|podman build --tag my-app .
The command is executed in the base directory of the Hilla app. The .
points to the same directory and the Dockerfile
in it.
The Docker image is then available and usable locally.
Run Docker container locally
A Docker container with the Hilla app can be started based on the Docker image created:
docker|podman run -it --rm --publish 8080:8080 my-app
Extends Dockerfile with build stage
Building the Hilla app as a native image can also be part of the Dockerfile
. For this purpose, the Dockerfile is divided into two stages:
# First stage: JDK with GraalVM and native image support
FROM ghcr.io/graalvm/native-image-community:21.0.2 AS build
WORKDIR /usr/src/app
# Copy project files
COPY . .
# Create native image
RUN ./mvnw clean package -Pproduction -Pnative native:compile
# Second stage: Lightweight debian-slim image
FROM debian:bookworm-slim
WORKDIR /app
# Copy the produced library files from the build stage
COPY --from=build /usr/src/app/target/*.so /app/
# Copy the native image from the build stage
COPY --from=build /usr/src/app/target/my-app /app/my-app
EXPOSE 8080
# Run the application
CMD ["/app/my-app"]
The native image is created in the first stage of the Dockerfile
. To do this, the required files and dependencies are copied into the Docker image, and then the Hilla app is built as a native image. In the second stage, the Docker image is created with the built native image.
Deploy Hilla app to fly.io
With the help of the Dockerfile
created, a Hilla app can be deployed very easily via a service such as fly.io. The registration at fly.io is free, but requires credit card details. Fly.io offers a free hobby plan, which allows you to deploy applications with limited resources free of charge. The app can then be accessed via https://<unique-app-identifier>.fly.dev
. The necessary configuration of IP addresses, DNS, SSL certificate etc. is handled by fly.io.
The path to deployment is described in detail in https://fly.io/docs/hands-on/.
First, the flyctl
is installed. Under macOS, this can be done with brew
, for example:
brew install flyctl
Afterwards, a new account is created with fly.io or an existing account is linked:
fly auth signup
A deployment configuration for fly.io is created before the first deployment. This is done using the command:
fly launch --dockerfile Dockerfile
The suggested settings can be adopted. A .dockerignore
should not be created initially, as this would prevent the native image and the libraries from being copied for the time being.
flyctl
stores the deployment configuration in the file fly.toml
. This file can be edited if required.
app = '<unique-app-identifier>'
primary_region = 'ams'
[build]
dockerfile = 'Dockerfile'
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
processes = ['app']
[[vm]]
size = 'shared-cpu-1x'
memory = '256mb'
The actual deployment of the Hilla app is then carried out using:
fly deploy
The Dockerfile
, the native image and libraries (.so
files) are uploaded to a build server of fly.io. The Docker image is created at fly.io. Fly.io stores the created Docker image in its own registry and then starts the Hilla app as a Docker container. After a successful start, the app can be called up in the browser via https://<unique-app-identifier>.fly.dev
.
If you want to use the Dockerfile
with the two stages as shown above, please note that the available resources of the Hobby plan will probably not be sufficient and the build process of the Docker image at fly.io may therefore fail.
Update Hilla app
If changes are made to the code of the Hilla app, it must be rebuilt and then deployed again:
./mvnw clean package -Pproduction -Pnative native:compile
fly deploy
If the native image is built via a separate stage in the Dockerfile
, only the execution of fly deploy
is required.
Summary
A Hilla app can be built very easily for deployment as a native image with the support of GraalVM. With the help of a suitable Dockerfile
, the deployment via a service such as fly.io is not a problem. The official Hilla documentation offers further instructions for deploying Hilla apps, e.g. to AWS, GCP, Azure or Heroku.
The Hilla blog also contains a blog post by Marcus Hellberg on this topic: Deploying a Spring Boot app as a native GraalVM image with Docker.
An example Hilla app that was build as native image and deployed to fly.io can be accessed via https://vaadin-create-countdown.fly.dev/. The corresponding source code is available at GitHub.