Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes
Sponsored Reserved space — layout preview until AdSense is connected

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

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:

  1. Developer pushes code → triggers GitLab CI
  2. CI runs unit tests (5 seconds)
  3. SonarQube scans for code smells (45 seconds)
  4. Docker build + push to Harbor (2 minutes)
  5. Ansible deploys to staging environment (30 seconds)
  6. Integration tests pass (1 minute)
  7. 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=admin123 to a public GitHub repo.
  • Build cache invalidation: Your CI will eventually download ubuntu:latest and 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.

Comments

Questions, corrections, and tips stay visible for everyone reading this page.

0 in thread

Join the discussion

Shown next to your comment.

Up to 4,000 characters

No comments yet

Be the first to leave a note — it helps the next reader.