CI/CD and GitOps for Java: Pipelines, Containers & Progressive Delivery

Writing the service is half the job; getting it to production safely, dozens of times a day, across hundreds of teams, is the other half. Enterprises that ship Java well treat delivery as a first-class engineering system: automated pipelines that build and verify every change, hardened container images, Git as the source of truth for what runs where, and deployment strategies that limit the blast radius of a bad release. This deep dive walks through CI/CD, GitOps, and progressive delivery for Java.

TL;DR: Automate build → test → scan → image → deploy on every commit. Build small, secure, layered (or native) images and scan them. Adopt GitOps — desired state in Git, an in-cluster agent (Argo CD/Flux) reconciles it — so deploys are pull requests and rollbacks are reverts. Reduce release risk with blue-green or canary deploys and decouple deploy from release using feature flags.
Tailor your resume to a DevOps / platform role →
flowchart LR
  Dev[Commit] --> CI[Build + Test + Scan]
  CI --> Img[Push image]
  Img --> Git[Update config in Git]
  Git --> Argo[Argo CD]
  Argo --> Cluster[Kubernetes: canary rollout]
        
A GitOps pipeline: CI builds the image, Git holds desired state, Argo CD reconciles the cluster.

The CI pipeline: fast, automated, trustworthy

Continuous integration means every push is built and verified automatically, so broken code is caught in minutes, not at release. A typical Java pipeline stages work so the fast checks fail first:

# GitHub Actions (sketch)
jobs:
  build:
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { distribution: temurin, java-version: '21', cache: maven }
      - run: ./mvnw -B verify          # compile + unit + integration tests
      - run: ./mvnw -B sonar:sonar     # static analysis / quality gate
      - run: ./mvnw spring-boot:build-image \
               -Dspring-boot.build-image.imageName=acme/orders:${{ github.sha }}
      - run: trivy image acme/orders:${{ github.sha }}   # vulnerability scan
      - run: docker push acme/orders:${{ github.sha }}

Keep it fast and ordered: unit tests in seconds on every push, integration/contract tests before merge, then image build, scan, and push. Tag images with the commit SHA so every deploy is traceable to exact source. Pipelines should be reproducible — pin tool versions, cache dependencies, and avoid “works on the runner” surprises.

Container images: small, layered, scanned

The artifact you deploy is a container image, and its quality is a security and performance concern. Use Spring Boot’s buildpacks or layered jars so dependency layers cache and only your thin app layer rebuilds (faster pulls, faster rollouts). Then harden it:

Secure the pipeline itself

The CI/CD system is a high-value target — it has the keys to production. Authenticate to your cloud and registry with short-lived OIDC federation rather than long-lived static keys, scope each pipeline’s permissions to least privilege, keep secrets in a manager (never in workflow files), and protect the main branch with required reviews and status checks. A compromised pipeline is a compromised production.

GitOps: Git as the source of truth

Traditional CD pushes changes into the cluster from the pipeline (which then needs broad cluster credentials). GitOps inverts this: the desired state of every environment lives in a Git repo (Kubernetes manifests or Helm/Kustomize), and an in-cluster agent — Argo CD or Flux — continuously pulls and reconciles the running state to match Git.

The benefits compound:

The common pattern: CI builds and pushes the image and opens a commit (often automated) bumping the image tag in the config repo; Argo CD detects the change and rolls it out.

Progressive delivery: limit the blast radius

“Deploy on Friday” is scary because a bad release hits 100% of users instantly. Progressive delivery shrinks that risk by rolling out gradually and watching.

StrategyHow it worksTrade-off
Rolling updateReplace pods a few at a time (Kubernetes default)Simple; both versions serve during rollout
Blue-greenStand up the new version fully, switch all traffic at onceInstant rollback; needs double capacity briefly
CanaryShift 1% → 5% → 50% → 100%, gated on metricsSmallest blast radius; needs good metrics + tooling

Tools like Argo Rollouts or Flagger automate canaries: they shift traffic in steps and automatically roll back if error rate or latency (from Prometheus) breaches a threshold — so a regression is caught at 5% of traffic, not 100%.

Decouple deploy from release with feature flags

The most powerful risk-reduction move is to separate deploying code from releasing a feature. With feature flags (LaunchDarkly, Unleash, Flagsmith, or a home-grown toggle), you ship dormant code to production and turn it on later — for internal users first, then a percentage, then everyone. A bad feature is disabled with a flag flip in seconds, no redeploy. This also enables trunk-based development (merge incomplete work behind a flag) and clean A/B testing. The discipline: keep flags short-lived and remove them once a feature is fully rolled out, or they become permanent complexity.

Database changes in the pipeline

Schema changes are the part of CD most likely to cause an outage, because the database is shared state that can’t be blue-green-switched. Run versioned migrations (Flyway/Liquibase) as a controlled pipeline step and use the expand/contract pattern so old and new app versions both work during a rolling deploy — covered in depth in our database migrations guide. Never let an app deploy and an incompatible schema change race each other.

Observability closes the loop

Continuous delivery without observability is flying blind. Tie deploys to your monitoring: annotate dashboards with release markers, and define the SLOs (error rate, latency) that gate a canary and trigger automatic rollback. The goal is a system where a regression is detected and reverted automatically, often before anyone notices — the deployment pipeline and the observability stack working as one feedback loop.

Takeaways

Shipping Java continuously at enterprise scale is an engineered system, not a script: fast, secure pipelines producing hardened, scanned images; GitOps making Git the auditable source of truth with reverts for rollbacks; progressive delivery (blue-green/canary) plus feature flags to shrink and control release risk; disciplined database migrations; and observability that closes the loop with automatic rollback. Build that, and deploying becomes a routine, low-drama event rather than a quarterly act of courage.

Frequently asked questions

What is GitOps?
GitOps is a deployment model where the desired state of your system lives in Git and an in-cluster agent (Argo CD or Flux) continuously reconciles the running environment to match it. Deploys become pull requests, rollbacks become git reverts, and the cluster self-heals drift — all auditable through Git history.

What is the difference between blue-green and canary deployments?
Blue-green runs two full environments and switches all traffic from the old (blue) to the new (green) at once, with instant rollback by switching back. Canary shifts a small percentage of traffic to the new version, watches metrics, and gradually increases it — limiting blast radius if the new version misbehaves.

Land your next Java role — tailor your resume with AI →