HOW TO BUILD A CI/CD PIPELINE USING JENKINS AND DOCKER (STEP-BY-STEP GUIDE)

How to Build a CI/CD Pipeline Using Jenkins and Docker (Step-by-Step Guide)

Building a Simple Jenkins CI/CD Pipeline for a Dockerized Flask App

I recently built my first real-world CI/CD pipeline using Jenkins, Docker, and Flask — and I want to share the journey with you.

What I Wanted to Achieve

Automatically build, test, and deploy a Flask app every time I push code * Use Jenkins for automation * Containerize the app with Docker * Run everything on my local machine

How I Did It

  1. Set up the project Created a basic Flask app and wrote tests using pytest. I also added a requirements.txt file listing dependencies.

  2. Created a Jenkinsfile pipeline Defined stages:

  3. Clone the GitHub repo

  4. Install dependencies (pip install -r requirements.txt)
  5. Run tests (pytest)
  6. Build Docker image
  7. Run Docker container

  8. Installed Jenkins and Docker on my RHEL machine Made sure Jenkins can run Docker commands (added Jenkins user to Docker group).

  9. Pushed the code to GitHub Hosted the repo publicly to make integration easier.
  10. Connected Jenkins with GitHub using Webhooks and Ngrok Since Jenkins runs locally, I used ngrok to expose my localhost to the internet. This allowed GitHub to notify Jenkins instantly when I push changes.
  11. Configured Jenkins job to trigger on webhook events Enabled GitHub hook trigger for GITScm polling in Jenkins.

Challenges I Faced

  • Git clone error — Jenkins couldn't find the repo initially because I pointed it to a local folder instead of the GitHub URL.

  • pytest not found — Jenkins didn't have pytest installed by default. Fixed by adding pytest to requirements.txt and installing dependencies in the pipeline.

  • Python import errors — Tests couldn’t find my Flask app modules because PYTHONPATH wasn’t set. Added export PYTHONPATH=$PWD before running tests.

  • Werkzeug version incompatibility — Tests failed with AttributeError: module 'werkzeug' has no attribute 'version'. Fixed by pinning Werkzeug to a version below 3.0 in requirements.txt.

  • Local Jenkins not reachable from GitHub — Solved by using ngrok to create a public tunnel to my localhost.

The Outcome

Now, every time I push code to GitHub:

  • Jenkins automatically clones the repo
  • Installs dependencies
  • Runs tests
  • Builds a Docker image
  • Runs the Flask app in Docker
  • All without me clicking a button. It’s a fully automated pipeline on my local machine!

Why This Matters

This project taught me how real CI/CD pipelines work in practice, and gave me a solid foundation to build more advanced DevOps skills. If you're starting your DevOps journey, I recommend building a similar pipeline. It covers all core concepts and tools without being overwhelming.

Code Repo: here

×