tutorial

Monitoring Java Container Builds with Jib and Vigilmon: Registry Health, Build Pipeline Heartbeats, TLS Certificate Alerts & Image Availability Checks

How to use Jib and Vigilmon together to monitor Java container image build pipelines — registry API health checks, OCI image push validation, TLS certificate expiry, and heartbeat monitoring for Jib-based Maven and Gradle CI/CD automation.

Jib is Google's container image builder for Java applications — a Maven and Gradle plugin that builds optimized OCI images from Java projects without requiring a Docker daemon, a Dockerfile, or any Docker installation at all. It layers the image into separate JVM dependencies, resources, and class files for fast incremental builds and efficient registry pushes. Used widely in Java microservice deployments on Kubernetes and Cloud Run, Jib pushes images directly to container registries from the build tool. When the target registry goes down, when TLS certificates expire, or when a scheduled Jib build pipeline silently fails, teams discover the problem only when Kubernetes cannot pull the next deployment. Vigilmon gives you external visibility into the container registries Jib pushes to, TLS certificate health for those endpoints, and heartbeat monitoring for the Maven and Gradle build pipelines that publish your Java service images.

What You'll Build

  • HTTP monitors on the OCI registry API endpoints Jib pushes images to
  • SSL certificate monitors for each registry's HTTPS endpoint
  • TCP monitors on registry ports for network-layer health
  • Heartbeat monitors for Jib-based Maven and Gradle CI/CD build pipelines
  • Alerting runbook mapping Jib failure modes to registry and build health issues

Prerequisites

  • A Java project using Jib via Maven (jib-maven-plugin) or Gradle (com.google.cloud.tools.jib)
  • One or more OCI-compatible container registries (GCR, GHCR, ECR, Artifact Registry, or self-hosted)
  • A free account at vigilmon.online

Step 1: Understand the Jib + Registry Health Surface

Jib communicates with OCI registries directly from the Maven or Gradle process — no Docker daemon involved. During a build, Jib:

  1. Pulls the base image layers from the base image registry
  2. Computes layer digests for the Java dependencies, resources, and classes
  3. Uploads only the changed layers to the target registry
  4. Publishes the image manifest to finalize the push

Every step requires registry availability:

# Maven: build and push with Jib
mvn compile jib:build
# Expected: Built and pushed image as gcr.io/myproject/myapp

# Gradle: build and push with Jib
./gradlew jib
# Expected: Containerizing application to gcr.io/myproject/myapp...

# Build to local Docker daemon (no registry needed)
mvn compile jib:dockerBuild

# Check the target registry is reachable before running Jib
curl -I https://gcr.io/v2/
# Expected: 401 (registry is up, auth required)

# Verify the pushed image exists
curl -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  "https://gcr.io/v2/myproject/myapp/manifests/latest"
# Expected: JSON manifest

The key failure signals to monitor:

  1. Target registry v2 API availability (for image layer uploads and manifest push)
  2. Base image registry availability (for pulling the base JVM image)
  3. TLS certificate validity on registry endpoints
  4. Build pipeline heartbeats for Maven and Gradle Jib jobs

Step 2: Monitor the Registry v2 API

The /v2/ endpoint is the OCI registry health signal Jib uses for all registry interactions:

# Google Container Registry
curl -I https://gcr.io/v2/
# Expected: 401

# Google Artifact Registry
curl -I https://us-central1-docker.pkg.dev/v2/
# Expected: 401 or 200

# GitHub Container Registry
curl -I https://ghcr.io/v2/
# Expected: 401

# AWS ECR (requires auth — test the public endpoint)
curl -I https://public.ecr.aws/v2/
# Expected: 200 or 401

# Self-hosted registry
curl -I https://registry.example.com/v2/
# Expected: 200 or 401

Create a Vigilmon monitor for your primary Jib target registry:

  1. Log in to VigilmonAdd Monitor → HTTP.
  2. URL: https://gcr.io/v2/ (or your registry's v2 endpoint).
  3. Check interval: 60 seconds.
  4. Response timeout: 10 seconds.
  5. Expected status codes: 200, 401.
  6. Label: Jib target registry v2 API.
  7. Click Save.

Jib also pulls a base image (typically eclipse-temurin, distroless/java, or a custom base). Add a monitor for the base image registry:

  1. Add Monitor → HTTP.
  2. URL: https://registry-1.docker.io/v2/ (if using Docker Hub base images) or https://gcr.io/v2/ for distroless.
  3. Expected status codes: 200, 401.
  4. Label: Jib base image registry.
  5. Click Save.

Step 3: Monitor the Registry TCP Port

Add a TCP monitor to catch network-layer failures separately from HTTP application failures:

# Test TCP connectivity to GCR
nc -zv gcr.io 443
# Expected: succeeded

# Test Google Artifact Registry
nc -zv us-central1-docker.pkg.dev 443
# Expected: succeeded

# Test self-hosted registry
nc -zv registry.example.com 443
# Expected: succeeded
  1. Add Monitor → TCP.
  2. Host: gcr.io (or your registry host).
  3. Port: 443.
  4. Check interval: 60 seconds.
  5. Label: Jib registry TCP (443).
  6. Click Save.

Step 4: Monitor TLS Certificates

Jib uses HTTPS for all registry communication and validates TLS certificates by default. An expired certificate causes all jib:build runs to fail with a TLS handshake error, across all developers and all CI pipelines:

# Check certificate on GCR
echo | openssl s_client -connect gcr.io:443 2>/dev/null | \
  openssl x509 -noout -dates
# notAfter=Dec 31 00:00:00 2026 GMT

# Check certificate on Artifact Registry
echo | openssl s_client -connect us-central1-docker.pkg.dev:443 2>/dev/null | \
  openssl x509 -noout -dates

# Find all Jib target registries in the project
grep -rE 'to\s*\{|toImage|image' pom.xml build.gradle build.gradle.kts \
  | grep -oP '[a-z0-9.-]+\.(io|com|pkg\.dev)[/:]' \
  | sed 's|[/:]||' | sort -u
# Output: gcr.io us-central1-docker.pkg.dev ghcr.io
  1. Add Monitor → SSL Certificate.
  2. Domain: gcr.io (repeat for each registry domain Jib uses).
  3. Alert when expiry is within: 30 days.
  4. Alert again: 14 days, 7 days, 3 days, 1 day.
  5. Click Save.

Add SSL certificate monitors for every registry hostname configured in your pom.xml or build.gradle Jib plugin sections.


Step 5: Heartbeat Monitoring for Jib Build Pipelines

Jib runs inside CI/CD pipelines on every commit or on a schedule. These pipelines produce no HTTP endpoints Vigilmon can probe directly — heartbeat monitoring detects when they stop running.

Maven Build in GitHub Actions

A workflow that builds and pushes a Java service image on every main branch merge:

# .github/workflows/jib-build.yml
name: Build and push Java image

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

      - name: Configure Docker for GCR
        run: gcloud auth configure-docker gcr.io

      - name: Build and push with Jib
        run: mvn compile jib:build -Djib.to.image=gcr.io/myproject/myapp:${{ github.sha }}

      - name: Ping Vigilmon heartbeat
        if: success()
        run: curl -s https://vigilmon.online/heartbeat/abc123

Set up the heartbeat monitor:

  1. In Vigilmon → Add Monitor → Cron Heartbeat.
  2. Expected interval: 1440 minutes (expected at least once per day with regular commits).
  3. Copy the heartbeat URL and paste it as the last workflow step.
  4. Label: Jib Maven build — main branch.
  5. Click Save.

Gradle Build Pipeline

For Gradle-based Java projects:

# .github/workflows/jib-gradle.yml
name: Build Java image (Gradle)

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'

      - name: Build and push with Jib
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          ./gradlew jib \
            -Djib.to.image=ghcr.io/${{ github.repository }}:${{ github.sha }} \
            -Djib.to.auth.username=${{ github.actor }} \
            -Djib.to.auth.password=${{ secrets.GITHUB_TOKEN }}

      - name: Ping Vigilmon heartbeat
        if: success()
        run: curl -s https://vigilmon.online/heartbeat/def456
  1. Add Monitor → Cron Heartbeat.
  2. Expected interval: 1440 minutes.
  3. Label: Jib Gradle build — main branch.
  4. Click Save.

Scheduled Nightly Multi-Service Build

For projects with multiple microservices built on a schedule:

#!/bin/bash
# nightly-jib-build.sh

SERVICES=("api-service" "worker-service" "scheduler-service")
REGISTRY="gcr.io/myproject"
TAG=$(date +%Y%m%d)

FAILED=0
for SERVICE in "${SERVICES[@]}"; do
  cd "/opt/src/${SERVICE}"
  if ! mvn compile jib:build -Djib.to.image="${REGISTRY}/${SERVICE}:${TAG}" -q; then
    echo "FAIL: ${SERVICE} image build failed"
    FAILED=1
  else
    echo "OK: ${REGISTRY}/${SERVICE}:${TAG}"
  fi
done

if [ "${FAILED}" -eq 0 ]; then
  curl -s https://vigilmon.online/heartbeat/ghi789
else
  echo "One or more builds failed — not pinging heartbeat"
  exit 1
fi
  1. Add Monitor → Cron Heartbeat.
  2. Expected interval: 1500 minutes (nightly build at 02:00 with 60-minute grace).
  3. Label: Nightly multi-service Jib build.
  4. Click Save.

Step 6: Validate Image Availability After Build

Verify that Jib-pushed images remain pullable after each build run:

#!/bin/bash
# validate-jib-images.sh — run every 30 minutes via cron

REGISTRY="gcr.io/myproject"
IMAGES=("api-service" "worker-service" "scheduler-service")

FAILED=0
for IMAGE in "${IMAGES[@]}"; do
  if ! crane digest "${REGISTRY}/${IMAGE}:latest" > /dev/null 2>&1; then
    echo "FAIL: ${REGISTRY}/${IMAGE}:latest not reachable"
    FAILED=1
  else
    echo "OK: ${REGISTRY}/${IMAGE}:latest"
  fi
done

if [ "${FAILED}" -eq 0 ]; then
  curl -s https://vigilmon.online/heartbeat/jkl012
else
  echo "Image validation failed — not pinging heartbeat"
  exit 1
fi

Schedule via cron:

# /etc/cron.d/jib-image-validation
*/30 * * * * deploy /opt/scripts/validate-jib-images.sh >> /var/log/jib-validation.log 2>&1
  1. Add Monitor → Cron Heartbeat.
  2. Expected interval: 35 minutes.
  3. Label: Jib image availability check.
  4. Click Save.

Step 7: Configure Alerting

In Vigilmon under Settings → Notifications, configure alert channels and response runbooks:

| Monitor | Trigger | Immediate action | |---|---|---| | Target registry v2 API | Non-200/401 or timeout | Check GCR/Artifact Registry status page; all jib:build runs will fail at the push step | | Base image registry | Non-200/401 | Jib cannot pull the base JVM layer; builds fail before compilation | | Registry TCP | Connection refused | Check network connectivity and firewall rules to registry host | | TLS certificate | < 30 days | Rotate or renew certificate; test with curl -I https://registry/v2/ after renewal | | Maven build heartbeat | Missed ping | Check GitHub Actions workflow; look for JVM compilation errors, GCP auth failures, or OOM issues | | Gradle build heartbeat | Missed ping | Check Gradle build logs; verify GITHUB_TOKEN has packages: write permission | | Nightly build heartbeat | Missed ping | Check scheduled job logs; check if registry auth token expired mid-build | | Image availability heartbeat | Missed ping | Run crane digest for each image; check for accidental image deletion or registry GC |


Common Jib + Registry Failure Modes and What Vigilmon Catches

| Scenario | Vigilmon signal | |---|---| | Target registry API down | v2 API monitor fires; all jib:build and ./gradlew jib runs fail at push | | Base image registry (Docker Hub/GCR) unavailable | Base image registry monitor fires; Jib cannot pull the JVM base layer | | TLS certificate expired on target registry | SSL monitor fires at 30-day threshold; Jib returns TLS handshake errors | | Maven build broken by dependency change | Build heartbeat goes silent; mvn compile fails before Jib even runs | | GCP service account key expired | Build heartbeat fires; jib:build fails with 401 on the push step | | Nightly build cron misconfigured or disabled | Nightly heartbeat goes silent; service images stop being updated | | Image deleted from registry after push | Image availability heartbeat fires at next validation run | | ECR auth token expired (12-hour TTL) | Build heartbeat fires; docker login step in ECR-based pipelines fails | | GCR quota exceeded for image storage | Push step fails with 429 or 507; v2 API monitor may still show healthy | | Java OOM during mvn compile in CI | Build heartbeat fires; Maven process killed before reaching the Jib step |


Jib removes Dockerfiles and Docker daemon dependencies from Java container builds — but the OCI registries it pushes to and pulls from remain external dependencies that can fail independently. Vigilmon gives you external visibility into registry availability, TLS health, and the Maven and Gradle build pipelines that publish your Java service images. When GCR has an outage at 3am, when a GCP service account key expires silently, or when a nightly multi-service build fails because one dependency update broke the classpath, Vigilmon alerts you before stale images propagate to production.

Start monitoring your Jib build pipelines in under 5 minutes — register free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →