CI/CD for a Java + Spring App with Kafka and GitHub Actions
Build with Maven and run Spring tests against a real Kafka broker on every push.
This recipe tests a Spring Boot service that produces and consumes Kafka messages. CI runs the Maven build against a Kafka service container so integration tests exercise a real broker, then packages the jar.
What the pipeline does
- set up the JDK with Maven cache
- start a Kafka (KRaft) service container
- run mvn verify against the broker
- package the runnable jar
- upload the jar artifact
The workflow
A single-node Kafka in KRaft mode backs the integration tests. SPRING_KAFKA_BOOTSTRAP_SERVERS points the app at the service container.
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
services:
kafka:
image: apache/kafka:3.7.0
ports: ["9092:9092"]
options: >-
--health-cmd "/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list"
--health-interval 10s --health-timeout 5s --health-retries 10
env:
SPRING_KAFKA_BOOTSTRAP_SERVERS: localhost:9092
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
cache: maven
- run: mvn -B verify
- uses: actions/upload-artifact@v4
with:
name: app-jar
path: target/*.jarCaching and speed
setup-java with cache: maven restores ~/.m2 keyed on pom.xml. Spring context startup plus Kafka integration tests are slow; cheaper managed runners such as Latchkey (around 69% cheaper than GitHub-hosted) keep the suite fast and auto-retry a flaky broker image pull.
Deploying
The runnable jar is the deploy artifact. Add a deploy job that builds a Docker image around it (FROM eclipse-temurin:21-jre) and pushes to your registry, or deploys the jar to your container platform once verify passes on main.
Key takeaways
- Use a Kafka KRaft service container so integration tests hit a real broker.
- Point SPRING_KAFKA_BOOTSTRAP_SERVERS at the service in CI.
- Cache ~/.m2 to avoid re-downloading the dependency tree.