CI/CD and GitOps Pipeline with Jenkins, Docker Hub, and ArgoCD

An example pipeline flow is shown below. The developer pushes code to a GitHub repo, triggering Jenkins to build and push a Docker image and update Kubernetes manifests. ArgoCD then pulls the updated manifests (using GitOps principles) to deploy into the Kubernetes cluster.
graph TD
A[Developer] --> B[GitHub]
B --> C[Jenkins]
C --> D[Docker Hub]
C --> E[Update Kubernetes Manifests]
E --> F[GitHub Manifests]
F --> G[ArgoCD]
G --> H[Kubernetes Cluster]
ArgoCD is a “declarative, GitOps continuous delivery tool for Kubernetes”. In this flow, Git repositories are the source of truth. When Jenkins updates a manifest in the Git repo, ArgoCD automatically syncs it to the cluster.
1. Clone the Project Repository
Begin by cloning the Git repository containing the Flask app, Dockerfile, Jenkinsfile, and Kustomize manifests. For example:
git clone https://github.com/ShivamSoni1995/Flask-App-ArgoCD.git
This repo should include your Flask source (e.g. app.py
), Dockerfile
, a Jenkinsfile
, and a kustomize/
directory with Kubernetes manifests.
2. Flask App and Dockerfile
Create a simple Flask application (e.g. app/
app.py
) and a requirements.txt
. For example:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello from Flask!"
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
Include a Dockerfile
to containerize it. For instance:
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
This builds a Docker image with the Flask app. Push this code to GitHub so Jenkins can build it.
3. Kubernetes Manifests with Kustomize
Define your Deployment and Service YAML under a Kustomize base. These are the deployment and service YAML
# kustomize/base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app
labels:
app: flask-app
spec:
replicas: 2
selector:
matchLabels:
app: flask-app
template:
metadata:
labels:
app: flask-app
spec:
containers:
- name: flask-app
image: youruser/flask-app:v1 # initial tag
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: flask-app-svc
spec:
type: LoadBalancer
selector:
app: flask-app
ports:
- port: 80
targetPort: 80
Using Kustomize makes it easy to patch image tags. ArgoCD natively supports Kustomize applications.
Kustomization.yaml
# kustomize/base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
4. Jenkins Pipeline (CI)
Create a Jenkinsfile
(declarative pipeline) to build, tag, and push the Docker image, then update the manifest in Git. Key stages:
pipeline {
agent any
environment {
IMAGE = "shivamsoni1995/flask-app-argocd"
TAG = "v${BUILD_NUMBER}"
BRANCH = "main"
GIT_REPO = "https://github.com/ShivamSoni1995/Flask-App-ArgoCD.git"
}
stages {
stage('Checkout') {
steps {
git branch: "${BRANCH}", credentialsId: 'github-creds', url: "${GIT_REPO}"
}
}
stage('Build & Push Docker Image') {
steps {
withCredentials([usernamePassword(credentialsId: 'dockerhub-creds', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) {
sh "docker build -t ${IMAGE}:${TAG} ."
sh "echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin"
sh "docker push ${IMAGE}:${TAG}"
}
}
}
stage('Update Image in deployment.yml') {
steps {
sh "yq e '.spec.template.spec.containers[0].image = \"${IMAGE}:${TAG}\"' -i deployment.yml"
}
}
stage('Commit & Push updated deployment.yml') {
steps {
withCredentials([usernamePassword(credentialsId: 'github-creds', usernameVariable: 'GIT_USER', passwordVariable: 'GIT_PASS')]) {
sh """
git config user.name "jenkins-bot"
git config user.email "jenkins-bot@example.com"
git add deployment.yml
git commit -m "Update image tag to ${TAG} [ci skip]" || echo 'No changes'
git push https://${GIT_USER}:${GIT_PASS}@github.com/ShivamSoni1995/Flask-App-ArgoCD.git ${BRANCH}
"""
}
}
}
}
post {
always {
cleanWs()
}
}
}
This pipeline first builds youruser/flask-app:${BUILD_NUMBER}
and pushes to Docker Hub. It then clones the GitOps repo, updates the deployment.yaml
with the new image tag (using yq), commits, and pushes. Ensure you add your Docker Hub credentials (dockerhub-creds
) and any necessary GitHub credentials to Jenkins.
5. Register the GitOps Repository in ArgoCD
Install ArgoCD on your cluster and expose its server (e.g. via kubectl patch svc argocd-server
to LoadBalancer
). Get the initial admin password:
kubectl -n argocd get secret argocd-initial-admin-secret -o \
jsonpath="{.data.password}" | base64 -d
Then log in (web UI or CLI) using user admin
. In ArgoCD, connect the GitHub repo (the one with Kustomize manifests) so ArgoCD can pull it. For example, via CLI:
argocd login <ARGOCD_SERVER>
argocd repo add https://github.com/youruser/gitops-flask.git \
--username youruser --password <GitHubToken>
This adds the repo (see example syntax). Alternatively, create a Kubernetes secret of type argocd.argoproj.io/secret-type
: repository
with username
and password
for GitHub. ArgoCD will use this to fetch manifests.
6. Create the ArgoCD Application
Define an ArgoCD Application resource (or use the UI) that points to your manifest repo. Example flask-app-argocd.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: flask-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/ShivamSoni1995/Flask-App-ArgoCD
targetRevision: HEAD
path: .
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
This tells ArgoCD to deploy the kustomize/base
manifests from the repo into the default
namespace. (The format follows ArgoCD’s spec.) The automated
syncPolicy ensures ArgoCD applies changes immediately.
Apply this manifest with kubectl:
kubectl apply -n argocd -f argo-application.yaml
ArgoCD will then create and sync the app. In the ArgoCD UI you should see the flask-app
Application with a green sync status.
7. Run Jenkins and Verify Deployment
Trigger the Jenkins job (e.g. by pushing a change or manually). Jenkins will build the Docker image, push it, and commit the updated manifest. ArgoCD will detect the Git change and automatically sync to the cluster. You can verify by:
kubectl get pods -l app=flask-app
kubectl get svc flask-app-svc
The service’s external IP should expose your Flask app. Access it to confirm the deployment.
By combining Jenkins for CI (building images) and ArgoCD for CD (GitOps deployment), this pipeline automates the full workflow. Jenkins handles the multi-stage build/test cycle (Jenkins pipelines are Groovy scripts defining multi-stage workflows), and ArgoCD continuously reconciles the cluster with the git-stored manifests. This ensures reliable, automated delivery of the Flask application into Kubernetes.
Subscribe to my newsletter
Read articles from Shivam Soni directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Shivam Soni
Shivam Soni
🌟I enjoy sharing my insights and experiences with the community through blog posts and speaking engagements.📝💬 My goal is to empower teams to achieve operational excellence through DevOps best practices and a culture of continuous improvement.💪🌟