← All posts
CI/CDGitHub ActionsArgoCD

How I cut deploy time from 2 hours to 8 minutes

A step-by-step walkthrough of replacing a Jenkins monolith with GitHub Actions + ArgoCD, and what I learned about pipeline design along the way.

2025-02-018 min read

When I joined Xellar Biosystems, deployments took 2 hours. By the time I left, they took 8 minutes. Here's exactly how we got there.

The problem

The original pipeline was a Jenkins monolith — a single 800-line Groovy script that did everything sequentially. Build, test, scan, deploy. No parallelism, no caching, no incremental builds.

Every deploy felt like a gamble. You'd kick it off, go get coffee, come back, and either celebrate or start debugging.

Step 1 — Map the bottlenecks

Before touching anything, I timed every stage:

Stage Time
Docker build 42 min
Unit tests 28 min
Integration tests 31 min
Security scan 14 min
Deploy 5 min
Total 120 min

The Docker build was the obvious first target.

Step 2 — Fix the Docker build

The original Dockerfile was copying the entire repo before installing dependencies. Classic mistake — every code change invalidated the layer cache.

# Before — cache always invalidated
COPY . .
RUN npm install

# After — dependencies cached unless package.json changes
COPY package*.json ./
RUN npm install
COPY . .

That single change dropped the build from 42 minutes to 6 minutes.

Step 3 — Parallelize tests

GitHub Actions makes parallelism trivial with a matrix strategy:

jobs:
  test:
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    steps:
      - run: npm test -- --shard=${{ matrix.shard }}/4

Unit and integration tests now ran simultaneously across 4 runners. Combined test time: 59 minutes → 14 minutes.

Step 4 — Switch to ArgoCD for deployments

ArgoCD replaced the Jenkins deploy stage entirely. Instead of imperative kubectl apply commands inside a pipeline, ArgoCD watches the Git repo and reconciles the cluster state automatically.

Benefits:

  • Deploy stage dropped from 5 minutes to under 1 minute
  • Automatic rollback on health check failure
  • Full audit trail in Git

The result

Stage Before After
Docker build 42 min 6 min
Tests 59 min 14 min
Security scan 14 min 14 min
Deploy 5 min 1 min
Total 120 min 35 min

Wait — that's 35 minutes, not 8. The final jump came from running build, test, and scan in parallel rather than sequentially. With all three running simultaneously, the wall-clock time collapsed to 8 minutes.

What I learned

  1. Measure before you optimize. The Docker cache fix took 20 minutes and saved 36 minutes per deploy.
  2. Parallelism is almost always worth it. GitHub Actions makes it free.
  3. GitOps reduces deploy risk. ArgoCD's automatic rollback saved us twice in the first month.