Tutorial
Building a Complete DevOps Pipeline Using Open Source Tools (Without Breaking the Bank)
Step-by-step guide to building a production-grade DevOps pipeline with GitLab CE, Jenkins, Ansible, Docker, Kubernetes, SonarQube, Prometheus, Grafana, and Harbor — all open source and zero licensing cost.
June 2026 · 8 min read · 1 views · 0 hearts
Advertisement
Building a Complete DevOps Pipeline Using Open Source Tools (Without Breaking the Bank)
Let me guess: you've been looking at your team's deployment process and thinking, "Surely there has to be a better way than manually SSH-ing into 47 servers every Friday afternoon." Good news — there is. And the crazy part? You don't need to spend a dime on fancy enterprise tools.
Open source has matured to the point where you can build a production-grade DevOps pipeline that would make Google engineers nod in approval. Here's how to stitch together the pieces without selling your kidney to Red Hat.
The Stack: Your Open Source Avengers
Before we dive into the pipeline itself, let's meet the tools. Think of this as assembling your own open source Infinity Gauntlet:
- GitLab CE (or GitHub/Gitea) — Your code repository, CI runner, and source of truth
- Jenkins — The grumpy but reliable workhorse (or Drone CI if you prefer your CI dry)
- Ansible — Configuration management that doesn't require a PhD to read
- Docker + Docker Compose — Because "it works on my machine" stops being funny after the third production outage
- Kubernetes (Minikube for testing, K3s for lightweight prod) — For when your container count exceeds your sanity
- SonarQube — The code critic your junior devs will learn to love (or hate)
- Prometheus + Grafana — So your boss has something pretty to look at during reviews
- Harbor — Private Docker registry that doesn't judge your container naming
Phase 1: Version Control + CI Triggering
# .gitlab-ci.yml (or GitHub Actions equivalent)
stages:
- test
- build
- deploy
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_TOKEN $CI_REGISTRY
unit-tests:
stage: test
image: python:3.11
script:
- pip install -r requirements.txt
- pytest --cov=app
only:
- main
- develop
The beauty here? Every push to your dev branch automatically kicks off testing. No more "I forgot to run tests" excuses.
Phase 2: Static Analysis Without The Static
This is where SonarQube steps in to berate your code publicly:
sonar-scanner \
-Dsonar.projectKey=my_awesome_app \
-Dsonar.sources=. \
-Dsonar.host.url=http://sonarqube.internal \
-Dsonar.login=$SONAR_TOKEN
Pro tip: Set your quality gate to fail builds that add more than 5% new technical debt. Your future self will thank you when you're not hunting down a variable named xXx_final_final_v2_FINAL three months later.
Phase 3: Building Artifacts That Actually Work
Docker multi-stage builds are your secret weapon against bloated containers:
# Stage 1: Build
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user -r requirements.txt
# Stage 2: Production image
FROM python:3.11-alpine
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
CMD ["gunicorn", "app:app", "-b", "0.0.0.0:8000"]
Notice anything? Our production image is 120MB smaller than if we'd used the full slim image. That's less attack surface and faster deployments.
Phase 4: Deployment Without Downtime
Here's the Ansible playbook that handles rolling updates like a boss:
---
- name: Deploy application with zero downtime
hosts: app_servers
tasks:
- name: Pull latest container
docker_image:
name: "registry.internal/app:{{ version }}"
source: pull
- name: Start new container
docker_container:
name: "app_v{{ version }}"
image: "registry.internal/app:{{ version }}"
ports:
- "8081:8000"
state: started
- name: Health check new container
uri:
url: "http://localhost:8081/health"
status_code: 200
register: health
- name: Switch load balancer
shell: |
echo "backend new-server:8081" | socat unix-connect:/var/run/haproxy/admin.sock -
when: health.status == 200
- name: Stop old container
docker_container:
name: "app_v{{ old_version }}"
state: absent
This gives you zero-downtime deployments that handle rollbacks automatically. If the health check fails, your old container stays live.
Phase 5: Monitoring Your Baby
Prometheus rules that actually catch problems before your users do:
groups:
- name: app_alerts
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.01
for: 2m
annotations:
summary: "Error rate above 1% over the last 5 minutes"
- alert: SlowRequests
expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[1m])) > 2
annotations:
summary: "95th percentile response time > 2 seconds"
Grafana dashboard that will make your PM look at you with new eyes: HTTP 200s, latency percentiles, active users, container restarts, and deployment frequency all in one view.
The Real Magic: Making It All Sing Together
The pipeline flow looks like this:
- Developer pushes code → triggers GitLab CI
- CI runs unit tests (5 seconds)
- SonarQube scans for code smells (45 seconds)
- Docker build + push to Harbor (2 minutes)
- Ansible deploys to staging environment (30 seconds)
- Integration tests pass (1 minute)
- Auto-promote to production with canary traffic shift
Total pipeline time: ~5 minutes from commit to production
Compare that to your current "push to main, Slack the ops team, wait an hour, hope for the best" workflow. Saves roughly 7 sanity points per deploy.
Common Pitfalls (That Everyone Encounters)
- Secret management: Don't hardcode passwords. Use Ansible Vault or HashiCorp Vault. I learned this the hard way after pushing
production_db_password=admin123to a public GitHub repo. - Build cache invalidation: Your CI will eventually download
ubuntu:latestand break everything. Pin your base images. - docker-compose vs kubernetes: For 1-5 services, docker-compose is fine. For anything more, embrace the K8s learning curve.
The entire stack above costs exactly $0 in licensing. You'll spend maybe $50/month on cloud VPS instances to run it all, unless you're already running some servers for other stuff.
Your pipeline should be as boring as possible — every "interesting" thing means something probably caught fire. When done right, your open source DevOps pipeline runs so smoothly that managers forget it exists. And that's the highest compliment a system can receive.
Advertisement
Comments
Questions, corrections, and tips stay visible for everyone reading this page.
Join the discussion
No comments yet
Be the first to leave a note — it helps the next reader.