Deployment
UniTrack builds an OCI container image with Cloud Native Buildpacks and ships sample Docker Compose stacks for both H2 and PostgreSQL.
1. Building a container image
The docker Maven profile binds Spring Boot’s build-image goal to the package phase:
./mvnw -Pdocker -pl unitrack-web package
This produces a unitrack:<version> image without a Dockerfile. The image name and whether
to publish are controlled by the docker.image.name and docker.publish properties. For
cross-architecture builds, set the platform:
./mvnw -Pdocker -pl unitrack-web package \
-Dspring-boot.build-image.imagePlatform=linux/amd64
| Buildpack images run as a non-root user. When mounting a host volume for H2, make sure the data directory is writable by that user — the sample H2 stack does this with a small init container. |
2. Compose: H2 (self-contained)
deploy/compose.h2.yaml runs the app on embedded H2 with a persistent volume — no external
database. It also enables the demo seeder and sets the admin password, making it ideal for
evaluation:
docker compose -f deploy/compose.h2.yaml up -d
A mem_limit caps the container so the JVM heap is sized sensibly on small hosts. Copy
deploy/.env.example to .env to override ports, memory and passwords.
3. Compose: PostgreSQL
deploy/compose.postgres.yaml runs the app together with a PostgreSQL container for a more
production-like setup (no demo data):
docker compose -f deploy/compose.postgres.yaml up -d
4. Remote deploy over SSH
scripts/deploy-remote.sh builds the image locally and ships it to a remote Docker host over
an SSH connection (docker save | docker -H ssh://… load), then brings up the chosen Compose
stack and health-checks it — handy when the build host and the deploy host differ:
scripts/deploy-remote.sh --host [email protected] --stack h2 --port 8081
| Flag | Meaning |
|---|---|
|
SSH target |
|
|
|
Host port to expose. |
|
Image platform, e.g. |
|
Compose project name. |
|
Reuse the already-built local image. |
5. Kubernetes (Helm)
deploy/helm/unitrack is a self-contained Helm chart — the app plus an optional bundled
PostgreSQL (postgresql.enabled, on by default; turn it off to use an external database). It
needs no external chart dependencies. See deploy/helm/unitrack/README.md for the full value
reference.
5.1. Publish the image
The cluster pulls the app from a registry, so publish there instead of building locally. Two ways to build:
Podman / Docker (recommended; works under rootless Podman): build the jar, then build
deploy/Containerfile (a JRE + fat-jar image) and push:
podman login registry.example.com:5000
./mvnw -pl unitrack-web -am package -DskipTests
podman build -t registry.example.com:5000/unitrack:0.1.0-SNAPSHOT -f deploy/Containerfile unitrack-web/target
podman push --tls-verify=false registry.example.com:5000/unitrack:0.1.0-SNAPSHOT
Cloud Native Buildpacks: the docker Maven profile builds a layered image without a
Dockerfile, but needs a real Docker daemon (it fails under rootless Podman):
docker login registry.example.com:5000
./mvnw -Pdocker -pl unitrack-web -am package \
-Ddocker.image.name=registry.example.com:5000/unitrack:0.1.0-SNAPSHOT \
-Ddocker.publish=true
For GHCR, use …/unitrack:<tag> under ghcr.io/<owner> and log in to ghcr.io.
scripts/deploy-k8s.sh automates either path via --builder podman (default) or
--builder buildpacks.
5.2. Install with bundled PostgreSQL
helm upgrade --install unitrack deploy/helm/unitrack \
-n unitrack --create-namespace \
--set image.repository=registry.example.com:5000/unitrack \
--set image.tag=0.1.0-SNAPSHOT
The database password is auto-generated on first install and persisted in the chart Secret
across upgrades. The chart force-sets SPRING_DOCKER_COMPOSE_ENABLED=false (the app enables
Compose lifecycle for local dev), wires liveness/readiness to the actuator probe endpoints, and
adds a generous startup probe so Flyway migrations don’t trip liveness on first boot.
When the cluster’s default StorageClass uses NFS with all_squash, PostgreSQL initdb
can fail its permission checks. Point the DB volume at a node-local class instead, e.g.
--set postgresql.persistence.storageClass=local-path.
|
5.3. Install against an external database
helm upgrade --install unitrack deploy/helm/unitrack \
-n unitrack --create-namespace \
--set postgresql.enabled=false \
--set externalDatabase.url='jdbc:postgresql://db:5432/unitrack' \
--set externalDatabase.user=unitrack \
--set externalDatabase.existingSecret=unitrack-db
5.4. One-shot helper
scripts/deploy-k8s.sh builds + publishes the image, runs helm upgrade --install, waits for
the rollout and runs helm test — the Kubernetes counterpart to scripts/deploy-remote.sh:
scripts/deploy-k8s.sh --registry registry.example.com:5000 --namespace unitrack
For a specific cluster (private registry, ingress host, storage class), capture those overrides in
your own values file and pass it with --values my-values.yaml.