This workshop will guide you through building a local GitOps pipeline using:
- Kind: for a local Kubernetes cluster
- ArgoCD: to handle GitOps
- GitHub: as the source of truth for your app deployment
Make sure you have these installed:
- Docker
- Kind
- kubectl
- Helm
- GitHub CLI (optional)
- A GitHub repository with a Kubernetes manifest or Helm chart
🔐 If your repo is private, you’ll also need a GitHub Personal Access Token (PAT) with the repo
scope. You can create one at https://github.com/settings/tokens.
Install Kind (if not already installed):
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.22.0/kind-$(uname)-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
Then create a simple cluster:
kind create cluster --name gitops-demo
✉️ This will create a local Kubernetes cluster named
gitops-demo
.
To expose the ArgoCD UI on port 8080 later, we’ll use port forwarding in Step 3.
Add the Argo Helm repo:
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
Create the namespace:
kubectl create namespace argocd
Now install ArgoCD with GitHub PAT for private repo access:
helm install argocd argo/argo-cd \
--namespace argocd \
--set server.service.type=ClusterIP \
--set "configs.credentialTemplates.https-creds.url=https://github.com/YOUR_USERNAME" \
--set "configs.credentialTemplates.https-creds.username=YOUR_GITHUB_USERNAME" \
--set "configs.credentialTemplates.https-creds.password=YOUR_GITHUB_PAT"
🛡️ Replace
YOUR_USERNAME
,YOUR_GITHUB_USERNAME
, andYOUR_GITHUB_PAT
with your actual values.
⚠️ Avoid hardcoding credentials in Helm commands. Consider using Kubernetes Secrets for better security.
Forward port:
kubectl port-forward svc/argocd-server -n argocd 8080:80
Open: http://localhost:8080
📋 Default login:
kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath="{.data.password}" | base64 -d && echo
Username: admin
Password: (above output)
Create a gitops-app.yaml
like below in your repo:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: 'https://github.com/YOUR_USERNAME/YOUR_REPO'
targetRevision: main
path: k8s # folder containing manifests or Helm chart
destination:
server: 'https://kubernetes.default.svc'
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
Apply it to ArgoCD:
kubectl apply -f gitops-app.yaml -n argocd
ArgoCD will now monitor your GitHub repo and automatically deploy changes!
- Modify your microservice code or deployment manifest
- Commit & push the change to GitHub
- Watch ArgoCD detect and sync the update 🎯
Use ArgoCD UI or:
kubectl get applications -n argocd
In ArgoCD UI, go to your app → App Details → "Enable Auto-Sync"
You now have a local GitOps pipeline running with:
- Kind (Kubernetes)
- ArgoCD (GitOps controller)
- GitHub (source of truth)
To delete the cluster:
kind delete cluster --name gitops-demo
📁 your-repo
└── gitops-app.yaml
📅️ k8s
├── deployment.yaml
├── service.yaml
In real-world organizations, application code and Kubernetes deployment manifests are kept in separate repositories.
This separation:
- Improves security (developers can’t accidentally change production manifests)
- Keeps infra changes reviewable by platform teams
- Enables CI pipelines to manage deployment promotion
- Allows ArgoCD to only track a safe, manifest-only repo
Contains:
- Source code (
src/
,services/
, etc.) - Dockerfile
deployment/
folder (developer-owned patches: HPA, resource limits, ingress, etc.)
Example:
📁 my-service-repo
├── src/
├── Dockerfile
├── deployment/
│ ├── base/ # Base deployment manifests
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ ├── hpa.yaml
│ └── overlays/
│ ├── dev/
│ │ ├── kustomization.yaml
│ │ └── patch-deployment.yaml
│ ├── prod/
│ │ ├── kustomization.yaml
│ │ └── patch-deployment.yaml
Contains:
- Only Kubernetes manifests that ArgoCD will sync
- Usually structured by environment
- Can be auto-updated by CI/CD pipelines
📁 my-argocd-manifests
├── dev/
│ └── my-service/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── hpa.yaml
├── prod/
│ └── my-service/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── hpa.yaml
Here’s the typical enterprise GitOps workflow:
- Developer Workflow
- Dev writes code → commits to code repo
- Adds/updates Kubernetes patches (HPA, resource limits, env vars) in
deployment/overlays/dev/
- Pushes code to a feature branch → opens Pull Request
- CI Pipeline (Build & Patch)
-
Builds Docker image → pushes to registry (tagged with commit SHA or version)
-
Runs
kustomize build deployment/overlays/dev/
→ updates image tag in manifest
-
Pushes updated manifest to ArgoCD manifest repo under
dev/
folder
-
- ArgoCD Sync
- ArgoCD is watching the manifest repo (e.g.,
dev/
branch) - Detects the new manifest commit
- Automatically deploys updated service to the dev cluster
- ArgoCD is watching the manifest repo (e.g.,
- Promotion to Higher Environments
- Once tested in
dev
, CI merges the manifest changes intoprod/
- ArgoCD deploys to production
- Once tested in