A complete CI/CD pipeline demonstration using Jenkins, ArgoCD, Docker, and Kubernetes
This repository showcases a production-ready DevOps workflow with automated builds, containerization, and GitOps-based deployments.
- Overview
- Architecture
- Prerequisites
- Quick Start
- Detailed Setup
- Pipeline Workflow
- Troubleshooting
- Best Practices
- Contributing
- Additional Resources
- License
This project implements a complete CI/CD pipeline that:
- β Automatically builds Docker images on code commits
- β Pushes images to DockerHub registry
- β Updates Kubernetes manifests with new image tags
- β Deploys applications using GitOps principles via ArgoCD
- β Supports multiple environments (staging, production)
- β Includes monitoring with Prometheus and Grafana
Tech Stack:
- CI/CD: Jenkins
- Containerization: Docker
- Orchestration: Kubernetes
- GitOps: ArgoCD
- Registry: DockerHub
- Monitoring: Prometheus, Grafana, Alertmanager
βββββββββββββββ βββββββββββββββ βββββββββββββββ
β GitHub βββββββΆβ Jenkins ββββββΆβ DockerHub β
β (Source) β β (Build) β β (Registry) β
βββββββββββββββ βββββββββββββββ βββββββββββββββ
β
β Update Manifest
βΌ
βββββββββββββββ
β GitHub β
β (GitOps) β
βββββββββββββββ
β
β Sync
βΌ
βββββββββββββββ βββββββββββββββ
β ArgoCD βββββββΆβ Kubernetes β
β (Deploy) β β (Runtime) β
βββββββββββββββ βββββββββββββββ
β
βΌ
βββββββββββββββ
β Monitoring β
β (Prom/Graf) β
βββββββββββββββ
Workflow:
- Developer pushes code to GitHub
- Jenkins detects changes via webhook
- Jenkins builds Docker image and pushes to DockerHub
- Jenkins updates Kubernetes manifest with new image tag
- ArgoCD detects manifest changes
- ArgoCD syncs and deploys to Kubernetes cluster
- Monitoring stack tracks application health
Before starting, ensure you have:
- Docker (v24.0+) - Install Docker
- Kubernetes Cluster (v1.26+) - Options:
- Minikube (local testing)
- K3s/K3d (lightweight)
- EKS/GKE/AKS (cloud)
- OpenShift (optional)
- kubectl (v1.26+) - Install kubectl
- Git (v2.34+)
- GitHub account with repository access
- DockerHub account (free tier works)
- Basic understanding of Docker, Kubernetes, and CI/CD concepts
- Minimum: 4GB RAM, 2 CPU cores, 20GB disk space
- Recommended: 8GB RAM, 4 CPU cores, 50GB disk space
- Helm (v3.0+) - For advanced deployments
- ArgoCD CLI - For command-line operations
- ngrok - For local webhook testing
# Clone the repository
git clone https://github.com/eknathdj/devops-argocd-jenkins-app.git
cd devops-sample-app
# Build Docker image locally (optional test)
docker build -t devops-sample-app:local .
# Run locally to test
docker run -p 8080:8080 devops-sample-app:local
# Verify application
curl http://localhost:8080/healthFor full CI/CD setup, continue to Detailed Setup.
Option A: Using Docker Compose (Recommended)
Create jenkins/docker-compose.yml:
version: '3.8'
services:
jenkins:
image: jenkins/jenkins:lts
user: root
ports:
- "8080:8080"
- "50000:50000"
volumes:
- jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
environment:
- JAVA_OPTS=-Djenkins.install.runSetupWizard=false
command: >
bash -c "
apt-get update &&
apt-get install -y docker.io curl git &&
git config --global --add safe.directory '*' &&
/usr/local/bin/jenkins.sh
"
volumes:
jenkins_home:Start Jenkins:
docker-compose -f jenkins/docker-compose.yml up -dOption B: Using Docker CLI
docker run -d \
--name jenkins \
-p 8080:8080 -p 50000:50000 \
-v jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
--user root \
jenkins/jenkins:lts
# Install dependencies inside Jenkins container
docker exec -it jenkins bash
apt-get update && apt-get install -y docker.io curl git
git config --global --add safe.directory '*'
exit
docker restart jenkins- Get Initial Admin Password:
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword- Access Jenkins:
- Open browser:
http://localhost:8080 - Paste the admin password
- Click "Install suggested plugins"
- Create your admin user
- Open browser:
Go to Manage Jenkins β Plugins β Available plugins
Install these plugins:
- β Git Plugin (git operations)
- β Pipeline (pipeline support)
- β Docker Pipeline (Docker integration)
- β Credentials Binding (secure credential handling)
- β Workspace Cleanup (cleanWs support)
Click "Install without restart" and wait for completion.
-
Generate GitHub PAT:
- Go to GitHub β Settings β Developer settings β Personal access tokens β Tokens (classic)
- Click "Generate new token (classic)"
- Name:
Jenkins CI - Expiration: 90 days or No expiration
- Select scopes:
- β
repo(Full control of repositories) - β
workflow(Update GitHub Action workflows)
- β
- Click "Generate token"
β οΈ Copy the token immediately (you won't see it again!)
-
Add to Jenkins:
- Go to Jenkins β Manage Jenkins β Credentials β System β Global credentials
- Click "Add Credentials"
- Kind: Username with password
- Username:
eknathdj(your GitHub username) - Password: Paste your GitHub PAT
- ID:
github-creds - Description: GitHub Personal Access Token
- Click "Create"
- Add to Jenkins:
- Same path: Manage Jenkins β Credentials β System β Global credentials
- Click "Add Credentials"
- Kind: Username with password
- Username: Your DockerHub username
- Password: Your DockerHub password or access token
- ID:
dockerhub-creds - Description: DockerHub Registry Credentials
- Click "Create"
-
Create New Pipeline:
- Click "New Item"
- Name:
sample-app-pipeline - Select "Pipeline"
- Click "OK"
-
Configure Pipeline:
-
Description: CI/CD Pipeline for DevOps Sample App
-
Build Triggers:
- β Check "GitHub hook trigger for GITScm polling"
-
Pipeline Definition:
- Select "Pipeline script from SCM"
- SCM: Git
- Repository URL:
https://github.com/eknathdj/devops-argocd-jenkins-app - Credentials: Select
github-creds - Branch:
*/main - Script Path:
Jenkinsfile
-
Additional Behaviours:
- Add "Wipe out repository & force clone" (optional, for clean builds)
-
-
Click "Save"
- Go to your GitHub repository:
https://github.com/eknathdj/devops-sample-app - Click Settings β Webhooks β Add webhook
- Configure:
- Payload URL:
http://<YOUR_JENKINS_IP>:8080/github-webhook/- Replace
<YOUR_JENKINS_IP>with your Jenkins server IP - For local testing with public access, use ngrok:
ngrok http 8080 # Use the ngrok URL: https://xxxx.ngrok.io/github-webhook/
- Replace
- Content type:
application/json - Events: Select "Just the push event"
- Active: β Checked
- Payload URL:
- Click "Add webhook"
Ensure your repository has this structure:
devops-sample-app/
βββ .gitignore # Git ignore rules
βββ Dockerfile # Docker build instructions
βββ Jenkinsfile # CI/CD pipeline definition
βββ package.json # Node.js dependencies
βββ package-lock.json # Lockfile for dependencies
βββ server.js # Main application file
βββ healthcheck.js # Health check endpoint
βββ prometheus-alerts.yaml # Prometheus alerting rules
βββ devops-pipeline-dashboard.json # Grafana dashboard JSON
βββ SECURITY.md # Security policy
βββ README.md # This file
βββ argocd/
β βββ production-application.yaml # ArgoCD app for production
β βββ staging-application.yaml # ArgoCD app for staging
βββ argocd-servicemonitor.yaml # ArgoCD service monitor
βββ environments/
β βββ production/
β β βββ deployment.yaml # K8s deployment for production
β β βββ service.yaml # K8s service for production
β β βββ namespace.yaml # K8s namespace for production
β β βββ kustomization.yaml # Kustomize config for production
β βββ staging/
β βββ deployment.yaml # K8s deployment for staging
β βββ service.yaml # K8s service for staging
β βββ namespace.yaml # K8s namespace for staging
β βββ kustomization.yaml # Kustomize config for staging
β βββ servicemonitor.yaml # Service monitor for staging
βββ jenkins/
β βββ docker-compose.yml # Jenkins Docker Compose
β βββ init-scripts/ # Jenkins initialization scripts
βββ scripts/
β βββ install-argocd.sh # ArgoCD installation script
β βββ install-monitoring.sh # Monitoring stack installation
β βββ test-pipeline.sh # Pipeline testing script
βββ jenkins-servicemonitor.yaml # Jenkins service monitor
- Log in to DockerHub
- Click "Create Repository"
- Configure:
- Name:
devops-sample-app - Visibility: Public (or Private if preferred)
- Description: Sample DevOps application
- Name:
- Click "Create"
Your image will be: <username>/devops-sample-app
Edit the DOCKER_IMAGE environment variable in your Jenkinsfile:
environment {
DOCKER_IMAGE = "your-dockerhub-username/devops-sample-app"
// ... rest of config
}Use the provided installation script:
chmod +x scripts/install-argocd.sh
./scripts/install-argocd.shOr manually:
# Create namespace
kubectl create namespace argocd
# Install ArgoCD
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Wait for pods to be ready
kubectl wait --for=condition=available --timeout=300s deployment/argocd-server -n argocdOption A: Port Forward (Local Access)
kubectl port-forward svc/argocd-server -n argocd 8081:443Access at: https://localhost:8081
Option B: LoadBalancer (Cloud)
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
kubectl get svc argocd-server -n argocd# For ArgoCD 2.0+
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d && echo
# Username: admin
# Password: (output from above command)# Linux
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x /usr/local/bin/argocd
# macOS
brew install argocd
# Login via CLI
argocd login localhost:8081 --username admin --password <password> --insecureUse the pre-configured application manifests:
# For staging
kubectl apply -f argocd/staging-application.yaml
# For production
kubectl apply -f argocd/production-application.yamlOr create manually via UI/CLI as described in the original setup.
Use the provided installation script:
chmod +x scripts/install-monitoring.sh
./scripts/install-monitoring.shOr manually:
# Install Prometheus and Grafana
kubectl create namespace monitoring
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack \
-n monitoring
# Access Grafana
kubectl port-forward svc/prometheus-grafana -n monitoring 3000:80
# Default: admin / prom-operator- Access Grafana at
http://localhost:3000 - Go to Dashboards β Import
- Upload
devops-pipeline-dashboard.json - Select the Prometheus data source
Apply the service monitors:
kubectl apply -f jenkins-servicemonitor.yaml
kubectl apply -f argocd-servicemonitor.yaml
kubectl apply -f environments/staging/servicemonitor.yaml- Trigger: Push to GitHub β Webhook notifies Jenkins
- Setup: Jenkins configures Git and cleans workspace
- Checkout: Clone repository
- Build: Create Docker image tagged with build number
- Push: Upload image to DockerHub
- Update: Modify Kubernetes manifest with new image tag
- Commit: Push manifest changes back to GitHub
- Deploy: ArgoCD detects changes and syncs to Kubernetes
To manually trigger a build:
# In Jenkins UI: Click "Build Now" on your pipeline
# Or via CLI:
curl -X POST http://localhost:8080/job/sample-app-pipeline/build \
--user admin:<your-jenkins-token># Watch ArgoCD sync status
argocd app get sample-app-staging --refresh
# Watch Kubernetes pods
kubectl get pods -n staging -w
# Check application logs
kubectl logs -f deployment/sample-app -n staging
# Get service endpoint
kubectl get svc -n stagingCause: Git ownership/permission issue
Solution:
# Inside Jenkins container
docker exec -it jenkins bash
git config --global --add safe.directory '*'
exit
docker restart jenkinsCause: GitHub credentials invalid or expired
Solution:
- Generate new GitHub Personal Access Token (PAT)
- Update
github-credsin Jenkins with new PAT - Ensure PAT has
repoandworkflowscopes
Cause: Docker socket not mounted or permission issue
Solution:
# Ensure socket is mounted
docker run -v /var/run/docker.sock:/var/run/docker.sock ...
# Or give Jenkins user permission
docker exec -it jenkins bash
chmod 666 /var/run/docker.sockCause: Manifest changes not detected or sync policy issue
Solution:
# Force refresh
argocd app get sample-app-staging --refresh
# Manual sync
argocd app sync sample-app-staging
# Check diff
argocd app diff sample-app-stagingCause: Image not found in registry or auth issue
Solution:
# Verify image exists in DockerHub
# Check image name in deployment.yaml matches exactly
# For private repos, create imagePullSecret
kubectl create secret docker-registry dockerhub-secret \
--docker-server=https://index.docker.io/v1/ \
--docker-username=<username> \
--docker-password=<password> \
-n staging
# Add to deployment.yaml:
# imagePullSecrets:
# - name: dockerhub-secretCause: File path incorrect or sed command issue
Solution:
# Verify file exists
ls -la environments/staging/deployment.yaml
# Test sed command locally
sed -i 's|image: .*|image: new-image:tag|g' environments/staging/deployment.yaml# Jenkins logs
docker logs jenkins -f
# ArgoCD logs
kubectl logs -f deployment/argocd-server -n argocd
# Application logs
kubectl logs -f deployment/sample-app -n staging
# Describe pod for details
kubectl describe pod <pod-name> -n staging
# Check events
kubectl get events -n staging --sort-by='.lastTimestamp'-
Rotate Credentials Regularly
- GitHub PATs: Rotate every 90 days
- DockerHub tokens: Use access tokens instead of passwords
- Jenkins credentials: Audit quarterly
-
Use Secrets Management
# Store sensitive data in Kubernetes secrets kubectl create secret generic app-secrets \ --from-literal=api-key=xxx \ -n staging -
Enable RBAC
- Restrict Jenkins service account permissions
- Use least-privilege principle in Kubernetes
-
Use Build Caching
# In Dockerfile, order layers by change frequency COPY package.json . RUN npm install COPY . .
-
Parallel Stages
parallel { stage('Test') { ... } stage('Security Scan') { ... } } -
Conditional Deployment
when { branch 'main' expression { currentBuild.result == 'SUCCESS' } }
-
Separate Config from Code
- Keep manifests in
environments/directory - Use different branches for environments (optional)
- Keep manifests in
-
Version Everything
- Always use specific image tags (never
:latest) - Tag format:
v1.0.0-${BUILD_NUMBER}
- Always use specific image tags (never
-
Environment Parity
- Use same base manifests with overlays
- Consider using Kustomize or Helm
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Open a Pull Request
# Clone your fork
git clone https://github.com/eknathdj/devops-argocd-jenkins-app.git
# Add upstream remote
git remote add upstream https://github.com/eknathdj/devops-argocd-jenkins-app.git
# Create feature branch
git checkout -b feature/my-feature
# Make changes and test locally
docker build -t test:local .
docker run -p 8080:8080 test:local
# Commit and push
git add .
git commit -m "Description of changes"
git push origin feature/my-featureThis project is licensed under the MIT License - see the LICENSE file for details.
Eknath DJ
- GitHub: @eknathdj
- Repository: devops-sample-app
- Jenkins community for excellent CI/CD tooling
- ArgoCD team for GitOps innovation
- Kubernetes community for container orchestration
- Prometheus and Grafana teams for monitoring excellence
- All contributors to this project
If you encounter issues:
- Check the Troubleshooting section
- Search existing GitHub Issues
- Create a new issue with:
- Clear description of the problem
- Steps to reproduce
- Error logs
- Environment details (OS, versions, etc.)
β If this project helped you, please give it a star!
Last Updated: October 2025