In this project you will build a complete GitOps deployment platform using ArgoCD. You will install ArgoCD on a Kubernetes cluster, connect it to a Git repository that acts as the single source of truth for application state, and implement automated continuous deployment — every merge to main triggers a deployment without any pipeline step that kubectl applies manifests directly. This architecture eliminates the security problem of CI/CD pipelines holding cluster credentials and is the deployment model used by engineering teams at Atlassian, Zerodha, and most Kubernetes-native product companies. +----------------------------------------------------------+ | Developer | | | | | +-- git push --► GitHub (main branch) | | | | | v | | GitHub Actions CI | | (build, test, push image) | | | | | Updates image tag | | in manifests repo | | | | | v | | Git Manifests Repo | | (source of truth) | | ^ | | | polls every 3 minutes | | | or webhook on push | | ArgoCD (in cluster) | | | | | v | | Kubernetes Cluster | | (reconciles live state to Git state) | +----------------------------------------------------------+
With traditional push-based CI/CD, the pipeline holds a kubeconfig or service account token with broad cluster permissions. If the CI system is compromised — through a supply chain attack, a malicious PR, or stolen secrets — the attacker gains direct cluster access. GitOps inverts this. ArgoCD runs inside the cluster and pulls from Git. No external system has cluster credentials. The only way to change the cluster state is to change Git, which requires code review and is fully auditable. Drift — when someone runs `kubectl apply` directly without going through Git — is detected automatically and can be auto-corrected.
### Step 1: Prepare Your Kubernetes Cluster and Git Repositories You need two Git repositories. One for the application code (where developers push features), and one for the Kubernetes manifests (where ArgoCD reads from). Separating them is a GitOps best practice — it decouples the deployment concerns from the development concerns. ```bash ## Verify you have a running Kubernetes cluster kubectl cluster-info kubectl get nodes ## For local development, use kind or k3s ## Install kind if needed curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.22.0/kind-linux-amd64 chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind ## Create a local cluster kind create cluster --name gitops-demo --config - <<EOF apiVersion: kind.x-k8s.io/v1alpha4 kind: Cluster nodes: * role: control-plane * role: worker * role: worker EOF kubectl get nodes ## Expected: 3 nodes Ready (1 control-plane, 2 workers) ``` **Create the manifests repository structure on GitHub:** ``` gitops-manifests/ +-- apps/ | +-- web-app/ | +-- base/ | | +-- deployment.yaml | | +-- service.yaml | | +-- kustomization.yaml | +-- overlays/ | +-- staging/ | | +-- kustomization.yaml | +-- production/ | +-- kustomization.yaml +-- argocd/ +-- applications/ +-- web-app-staging.yaml +-- web-app-production.yaml ``` > 📌 **Remember:** The manifests repository is not the application code repository. Developers commit code to the app repo. The CI pipeline (or an automated tool like ArgoCD Image Updater) commits image tag changes to the manifests repo. ArgoCD watches only the manifests repo. ### Step 2: Install ArgoCD on Kubernetes ```bash ## Create the argocd namespace kubectl create namespace argocd ## Install ArgoCD — this creates all CRDs, deployments, services, and RBAC kubectl apply -n argocd -f \ https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml ## Wait for all pods to be Running (takes 2-3 minutes) kubectl wait --for=condition=available \ --timeout=300s \ deployment/argocd-server -n argocd ## Check all pods are running kubectl get pods -n argocd ## Expected: argocd-server, argocd-repo-server, argocd-application-controller, ## argocd-dex-server, argocd-redis all Running ## Get the initial admin password (auto-generated) kubectl -n argocd get secret argocd-initial-admin-secret \ -o jsonpath="{.data.password}" | base64 -d && echo ## Save this password — you will need it to log in ``` **Access the ArgoCD UI:** ```bash ## Forward the ArgoCD server port to your local machine kubectl port-forward svc/argocd-server -n argocd 8080:443 ## Open https://localhost:8080 in your browser ## Username: admin ## Password: (the value from the command above) ``` **Install the ArgoCD CLI:** ```bash curl -sSL -o argocd-linux-amd64 \ https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64 chmod +x argocd-linux-amd64 && sudo mv argocd-linux-amd64 /usr/local/bin/argocd ## Log in via CLI argocd login localhost:8080 \ --username admin \ --password YOUR_PASSWORD \ --insecure ``` ### Step 3: Create the Application Manifests with Kustomize Kustomize allows you to have a base configuration and environment-specific overlays — without duplicating YAML files. Create `apps/web-app/base/deployment.yaml` in your manifests repo: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: web-app spec: replicas: 2 selector: matchLabels: app: web-app template: metadata: labels: app: web-app spec: containers: * name: web-app image: nginx:1.25-alpine # ArgoCD Image Updater will manage this tag ports: * containerPort: 80 resources: requests: memory: "64Mi" cpu: "50m" limits: memory: "128Mi" cpu: "200m" readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 5 periodSeconds: 10 ``` Create `apps/web-app/base/kustomization.yaml`: ```yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: * deployment.yaml * service.yaml ``` Create the production overlay `apps/web-app/overlays/production/kustomization.yaml`: ```yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: * ../../base patchesStrategicMerge: * | apiVersion: apps/v1 kind: Deployment metadata: name: web-app spec: replicas: 3 # Production runs 3 replicas namePrefix: prod- namespace: production ``` Commit and push these files to your manifests repo. ### Step 4: Connect ArgoCD to Your Git Repository ```bash ## Add your manifests repository to ArgoCD ## For a public repo: argocd repo add https://github.com/YOUR_USERNAME/gitops-manifests.git ## For a private repo, use an SSH key or personal access token: argocd repo add https://github.com/YOUR_USERNAME/gitops-manifests.git \ --username YOUR_USERNAME \ --password YOUR_GITHUB_PAT ## Verify the repository was added successfully argocd repo list ## Expected: STATUS = Successful ``` ### Step 5: Create the ArgoCD Application Resources Create `argocd/applications/web-app-production.yaml` in your manifests repo: ```yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: web-app-production namespace: argocd finalizers: * resources-finalizer.argocd.argoproj.io # Deletes cluster resources when app is deleted spec: project: default source: repoURL: https://github.com/YOUR_USERNAME/gitops-manifests.git targetRevision: main path: apps/web-app/overlays/production destination: server: https://kubernetes.default.svc namespace: production syncPolicy: automated: prune: true # Delete resources removed from Git selfHeal: true # Revert manual changes to match Git state syncOptions: * CreateNamespace=true # Create namespace if it does not exist * PrunePropagationPolicy=foreground retry: limit: 3 backoff: duration: 5s factor: 2 maxDuration: 3m ``` ```bash ## Apply the Application resource kubectl apply -f argocd/applications/web-app-production.yaml ## Watch ArgoCD sync the application argocd app get web-app-production argocd app sync web-app-production # Manual sync for the first time ## Watch the live sync status argocd app wait web-app-production --health --sync ``` > ⚠️ **Security:** Enable `selfHeal: true` in production. Without it, a developer who runs `kubectl apply` directly to make an emergency change will cause permanent drift — the cluster state diverges from Git and nobody knows which state is the source of truth. ### Step 6: Implement Automated Image Updates with ArgoCD Image Updater ArgoCD Image Updater watches your container registry for new image tags and automatically commits the updated tag to your manifests repo. ```bash ## Install ArgoCD Image Updater kubectl apply -n argocd -f \ https://raw.githubusercontent.com/argoproj-labs/argocd-image-updater/stable/manifests/install.yaml ## Annotate the Application to enable image updates kubectl annotate application web-app-production -n argocd \ argocd-image-updater.argoproj.io/image-list="web-app=nginx" \ argocd-image-updater.argoproj.io/web-app.update-strategy=semver \ argocd-image-updater.argoproj.io/web-app.allow-tags="regexp:^1\.25\..*" ``` ### Step 7: Simulate a Rollback The power of GitOps is that rollback is a git revert. ```bash ## Simulate a bad deployment by updating the image to a non-existent tag ## Edit apps/web-app/overlays/production/kustomization.yaml ## Change the image to: nginx:99.99-broken git commit -am "test: use broken image tag" git push ## Watch ArgoCD detect the change and try to sync argocd app get web-app-production --watch ## The deployment will fail because the image does not exist ## Rollback via Git — the correct GitOps way git revert HEAD git push ## ArgoCD detects the revert and re-syncs to the working state argocd app wait web-app-production --health ``` > 💡 **Tip:** You can also use `argocd app history web-app-production` and `argocd app rollback web-app-production REVISION` to roll back to any previous revision from the ArgoCD CLI without touching Git. However, the Git revert method is preferred because it creates an audit trail in your repository.
```bash ## 1. Verify ArgoCD shows the app as Synced and Healthy argocd app get web-app-production ## Expected: Health Status = Healthy, Sync Status = Synced ## 2. Check the production pods are running kubectl get pods -n production ## Expected: 3 pods Running (prod-web-app-*) ## 3. Test drift detection — make a manual change kubectl scale deployment prod-web-app -n production --replicas=1 ## Wait 3 minutes (or trigger webhook) argocd app get web-app-production ## Expected: Sync Status = OutOfSync (ArgoCD detected the manual change) ## With selfHeal=true, ArgoCD will automatically revert this within 3 minutes ## 4. Verify selfHeal reverted the change kubectl get deployment prod-web-app -n production ## Expected: DESIRED = 3 (restored from Git state) ## 5. Verify no kubectl credentials exist in your CI system ## The entire deployment happened without any pipeline having cluster access echo "GitOps deployment complete — no cluster credentials in CI" ```
In this project you will build a complete GitOps deployment platform using ArgoCD. You will install ArgoCD on a Kubernet...
With traditional push-based CI/CD, the pipeline holds a kubeconfig or service account token with broad cluster permissio...
Step 1: Prepare Your Kubernetes Cluster and Git Repositories You need two Git repositories. One for the application code...
...
Aligns directly with DevOps, Site Reliability (SRE), and Platform Engineering job descriptions.