Optimizing GitLab CI Pipeline Speed with Multi-Stage Docker Builds

Improving GitLab CI Pipeline Speed by 95% with Multi-Stage Docker Builds One of my first tasks when I joined DTN was to fix a painfully slow build pipeline. The Docker build job was taking forever. We had a single massive Dockerfile building a mono-repo containing 7 applications (one web app + 6 Node.js services). The resulting image was huge — over 1GB of RAM and nearly 1000 CPU cycles when deployed to Kubernetes. Instead of trying to optimize that monster, I took a different approach: I broke each service and the web app into its own dedicated Dockerfile. Then I implemented intelligent change detection that only triggered the relevant pipelines. Each pipeline now runs in parallel and includes: - NPM cache - Lint + Build + Unit Tests - Docker build using multi-stage builds - Push to Artifactory - Deployment to AWS ECS / EKS (dev → staging → prod) Result? The pipeline went from over 3 hours down to under 8 minutes per changed codebase — running in parallel. The real game-changer was switching to multi-stage Docker builds. In a single build invocation, we: - Use a heavy build image to compile everything - Copy only the final compiled artifacts into a minimal runtime image (based on slim LTS Node) - Run the app as a least-privileged user This approach not only slashed build times and resource usage dramatically, but also improved security by reducing the attack surface of our containers. Multi-stage builds + intelligent pipeline triggering = faster feedback loops and much happier developers. Have you tried multi-stage Docker builds in your pipelines? What’s been your biggest win (or pain point) when optimizing CI/CD speed? #DevOps #Docker #GitLabCI #Kubernetes #AWS #CI_CD #MultiStageBuilds

That’s an impressive turnaround! It’s fascinating how breaking down a monolith can yield such drastic improvements. I’ve seen similar setups where optimizing just the caching mechanism can shave off even more time. Did you face any unexpected challenges during the transition?

Very nice indeed. Lots of cores doesn't help giant builds all that much, so splitting up generally helps a lot. Those are great results. I'm sure that made a tremendous boost to development efficiency. Have you collected some metrics to demonstrate your impact? I've tried to push beyond Docker caching, since it's a complete hit/miss, to provide incremental caching by pulling steps out of Docker and copying in the result. That way I can provide an incremental cache outside the Docker build. https://blog.cedarci.com/ultimate-docker-build/ I also ended up building my own container builder to workaround the remaining inherent slowness in Buildkit for most cases I've run into. https://blog.cedarci.com/fastest-container-build/ Some ideas for if/when that huge improvement becomes stale and you're hungry for more.

See more comments

To view or add a comment, sign in

Explore content categories