Master core concepts and production patterns.
Most teams deploy by running scripts manually or SSHing into servers. That works fine for two engineers. It breaks down completely for twenty. **GitOps** solves this by making Git the single source of truth for everything running in your cluster — if it is not in Git, it is not supposed to be running. **ArgoCD** watches a Git repository. When you merge a change — a new image tag, an updated replica count, a new environment variable — ArgoCD detects the difference between what Git says should be running and what is actually running in Kubernetes, then automatically pulls the cluster toward that desired state. [ Developer pushes code to GitHub ] | v [ GitHub Actions Pipeline ] (Test -> Build image -> Push to registry) | v [ Updates image tag in Config Repo ] | v [ ArgoCD detects manifest change ] | v [ ArgoCD syncs cluster to match Git ] | v [ New pods run, old pods terminate ] At companies like Zerodha and Razorpay, GitOps is exactly how production changes get shipped — a pull request into a config repo, not an engineer typing `kubectl apply` from their laptop at 11pm. > 💡 **Tip:** With GitOps, deployments become pull requests and rollbacks become `git revert`. The entire deployment history lives in your Git log, not scattered across terminal sessions.
Manual deployment — SSHing in, running a script, applying YAML by hand — has no audit trail, no easy rollback, and no protection against configuration drift. Someone changes a replica count directly in the cluster during an incident, forgets to update it anywhere else, and three weeks later staging and production quietly don't match anymore. GitOps solves this with two properties: the desired system state is stored in Git, and a software agent (ArgoCD) continuously reconciles the actual cluster state toward that desired state. This project also separates the **app repository** (source code, built by CI) from the **config repository** (Kubernetes manifests, watched by ArgoCD). That separation means the CI pipeline controls *what images exist*, while the config repo controls *what images are deployed* — and every production deployment is auditable from the config repo's Git history alone. By the end of this project, a change like "roll out orders-api v2.3 to production" will be a pull request that anyone on the team can review, approve, and revert — exactly like a code change. ---
Master core concepts and production patterns.
Before wiring up GitOps, you need something to deploy. We'll build a small Flask API — this stands in for a real service like Swiggy's order-status API or Razorpay's payment-status endpoint. It's intentionally simple so the focus stays on the deployment pipeline, not the app itself. The Dockerfile below uses a multi-stage build and runs as a non-root user — both are standard production practice, not optional extras.
```bash mkdir gitops-app cd gitops-app cat > app.py << 'EOF' from flask import Flask, jsonify import os app = Flask(__name__) @app.route('/') def home(): return jsonify({ 'service': 'orders-api', 'version': os.getenv('APP_VERSION', 'v1.0.0'), 'environment': os.getenv('ENVIRONMENT', 'production'), 'status': 'healthy' }) @app.route('/health') def health(): return jsonify({'status': 'ok'}), 200 if __name__ == '__main__': app.run(host='0.0.0.0', port=8080) EOF cat > requirements.txt << 'EOF' flask==3.0.0 gunicorn==21.2.0 EOF cat > Dockerfile << 'EOF' FROM python:3.12-slim AS builder WORKDIR /app COPY requirements.txt . RUN pip install --user --no-cache-dir -r requirements.txt FROM python:3.12-slim WORKDIR /app COPY --from=builder /root/.local /root/.local COPY app.py . RUN useradd --create-home appuser USER appuser ENV PATH=/root/.local/bin:$PATH CMD ["gunicorn", "--bind", "0.0.0.0:8080", "--workers", "2", "app:app"] EOF ``` Build and test locally: ```bash docker build -t orders-api:local . docker run -p 8080:8080 orders-api:local & curl http://localhost:8080/ curl http://localhost:8080/health ``` ```json {"environment": "production", "service": "orders-api", "status": "healthy", "version": "v1.0.0"} {"status": "ok"} ``` > 🔴 **Common Mistake:** Running the Flask development server (`app.run()`) directly in production instead of `gunicorn`. The dev server is single-threaded and not designed to handle concurrent traffic — always use a WSGI server like gunicorn for anything beyond local testing. ---
Master this concept and view production exercises.
Most teams deploy by running scripts manually or SSHing into servers. That works fine for two engineers. It breaks down ...
Manual deployment — SSHing in, running a script, applying YAML by hand — has no audit trail, no easy rollback, and no pr...
Master this concept and view production exercises.
Before wiring up GitOps, you need something to deploy. We'll build a small Flask API — this stands in for a real service...
Build and test locally: > 🔴 Common Mistake: Running the Flask development server (app.run()) directly in production ins...
Master this concept and view production exercises.
This is the repository ArgoCD will actually watch. It contains only Kubernetes manifests — no application source code at...
Push it to GitHub: > 💡 Tip: Naming the folder base/ even for a single-environment setup pays off later — when you add a...
Master this concept and view production exercises.
ArgoCD itself runs as a set of pods inside your cluster — a controller that continuously polls Git, a repo-server that c...
Access the UI: Open https://localhost:8080, accept the self-signed certificate warning, and log in with username admin a...
Master this concept and view production exercises.
An ArgoCD Application is the object that ties everything together: it tells ArgoCD which Git repo to watch, which path i...
Verify the sync: > 🔴 Common Mistake: Setting selfHeal: false allows configuration drift to persist indefinitely. If som...
Master this concept and view production exercises.
This pipeline has three jobs, and the order matters: test, then build-and-push, then update-manifests. Each job only run...
Add tests: Create a Fine-grained Personal Access Token scoped to write access on gitops-config only, and add it to the a...
Master this concept and view production exercises.
This is the payoff step — you don't run any deployment command yourself. After the pipeline updates the image tag in git...
then: then: Verify the new version is actually running: > 💡 Tip: Don't want to wait 3 minutes while testing? Run argocd...
Master this concept and view production exercises.
The real power of GitOps shows up here: rollback is just git revert. There's no special rollback tooling to learn — the ...
Simulate a bad deployment: The pipeline builds and deploys the broken version. Kubernetes marks the pods Unhealthy, and ...
Master this concept and view production exercises.
Run each of these to confirm the full pipeline works end to end....
Mistake Why It Breaks Fix App code and manifests in one repo Every code change triggers a deploy Keep app repo and confi...
Aligns directly with DevOps, Site Reliability (SRE), and Platform Engineering job descriptions.