# CI/CD for a Quarkus Native Image with GitHub Actions

> Set up CI/CD for a Quarkus app compiled to a native image with GitHub Actions: run JVM tests, build a GraalVM native binary, and ship a tiny container.

Source: https://latchkey.dev/learn/ci-recipes/ci-cd-for-a-quarkus-native-image-with-github-actions  
Updated: 2026-06-26

Test on the JVM, compile a GraalVM native binary, and ship a tiny container.

Quarkus compiles to a GraalVM native image for fast startup and low memory. This recipe runs JVM tests first, then builds the native binary and a minimal container.

## What the pipeline does

- set up a GraalVM JDK
- run JVM unit tests
- build the native image with -Pnative
- run native integration tests
- build and push a micro container

## The workflow

graalvm/setup-graalvm provides native-image. The native profile compiles ahead of time; in-container native builds avoid a local GraalVM but are slower, so a native GraalVM build is used here.

```.github/workflows/ci.yml
name: CI
on:
  push:
    branches: [main]
  pull_request:
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: graalvm/setup-graalvm@v1
        with:
          java-version: 21
          distribution: graalvm
          cache: maven
      - run: ./mvnw -B test
      - run: ./mvnw -B package -Pnative -DskipTests
      - run: ./mvnw -B verify -Pnative
      - name: Build native container
        run: docker build -f src/main/docker/Dockerfile.native -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
```

## Caching and speed

cache: maven restores ~/.m2. Native compilation is CPU and memory intensive and can take minutes, so this is exactly the step where cheaper managed runners such as Latchkey $0.0025/min at 2 vCPU against $0.006 GitHub-hosted pay off, and auto-retry covers transient GraalVM download or OOM blips on a retry.

## Deploying

The native binary runs in a tiny distroless or UBI-micro image, so cold starts are milliseconds. Push to your registry and deploy to Kubernetes, Cloud Run, or Knative where fast scale-to-zero matters.

## Making this reliable in CI

- Pin every tool version. An unpinned toolchain turns a runner image update into a build break on unchanged code.
- Cache what is expensive to produce, and key the cache to the tool version so a restore across a version boundary cannot poison the build.
- Assert on the produced artifact rather than on the command succeeding.
- Set an explicit timeout so a hung step fails fast instead of consuming the whole job budget.

## FAQ

### CI/CD for a Quarkus Native Image with GitHub Actions?

Quarkus compiles to a GraalVM native image for fast startup and low memory. This recipe runs JVM tests first, then builds the native binary and a minimal container.

### The workflow?

graalvm/setup-graalvm provides native-image. The native profile compiles ahead of time; in-container native builds avoid a local GraalVM but are slower, so a native GraalVM build is used here.

### Caching and speed?

cache: maven restores ~/.m2. Native compilation is CPU and memory intensive and can take minutes, so this is exactly the step where cheaper managed runners such as Latchkey $0.0025/min at 2 vCPU against $0.006 GitHub-hosted pay off, and auto-retry covers transient GraalVM download or OOM blips on a retry.

### Deploying?

The native binary runs in a tiny distroless or UBI-micro image, so cold starts are milliseconds. Push to your registry and deploy to Kubernetes, Cloud Run, or Knative where fast scale-to-zero matters.

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
