Module 5: Gitops With Argocd

2. Pull-based GitOps: an operator that watches, not a pipeline that pushes

Description

The previous lesson showed you the full citation, but still in the abstract: "an operator running inside the cluster constantly watches the Git repository and pulls the changes in." This lesson makes that mechanism concrete, with the same level of technical detail cicd-and-gitops-on-aws-guide M7.4 used to compare it against its own apply.yml — the exact difference of who initiates the movement, where the credentials travel, and how often a change gets applied. You still don't install anything in this lesson; you install the precise vocabulary that lessons 3 and 4 are going to turn into a real cluster.

Connection to the module

This lesson is the bridge between the introduction (lesson 1) and the first "hands-on" (lesson 3). Without the mechanism clear in your head, installing Gitea and ArgoCD would be following instructions without understanding what they solve — with the mechanism clear, every step in lessons 3-6 has a visible technical reason.


The mechanism, side by side with what you already know

If you completed cicd-and-gitops-on-aws-guide, you already saw this diagram — or one almost identical to it — in its Module 7, lesson 4. It's worth rebuilding it here, with this guide's exact names instead of generic ones:

   PUSH-BASED GITOPS (cicd-and-gitops-on-aws-guide, apply.yml)

   Developer              GitHub                  CI Pipeline          AWS
        │                    │                         │                │
        │──git push─────────▶│                         │                │
        │                    │──triggers apply.yml────▶│                │
        │                    │                         │──terraform apply──▶│
        │                    │                         │   (credentials     │
        │                    │                         │    travel OUT      │
        │                    │                         │    from here)      │
        │                    │◀────updated state───────│                │

   The PIPELINE initiates the movement. An event (push to main) triggers it.


   PULL-BASED GITOPS (this module, ArgoCD)

   You                     Gitea                   ArgoCD                andes-cargo-cluster
                       (inside the cluster)    (inside the cluster)     (the same cluster)
        │                    │                         │                     │
        │──git push─────────▶│                         │                     │
        │                    │◀── compares every few seconds ─────────────────│
        │                    │      (ArgoCD COMES to check, nobody pushes it) │
        │                    │────current repository─▶│                     │
        │                    │                         │──applies inside───▶│
        │                    │                         │   the SAME          │
        │                    │                         │   cluster it        │
        │                    │                         │   already runs on   │

   ArgoCD initiates the movement. An internal timer triggers it,
   not an external event. Gitea, ArgoCD, and andes-cargo-cluster are,
   all three, the same kind cluster — there's no external network
   boundary to cross.

The most important difference from cicd-and-gitops-on-aws-guide's diagram: there, CI Pipeline and AWS were two completely separate systems, with a real network boundary between them — the GitHub Actions runner isn't part of your AWS account. Here, Gitea, ArgoCD, and the cluster ArgoCD manages are, literally, the same kind create cluster. This isn't a lab simplification: it's actually more faithful to the real production pattern than cicd-and-gitops-on-aws-guide's AWS example — in a real EKS (Module 7 of this guide), ArgoCD also runs as Pods inside the very cluster it syncs, even though the Git repository normally lives on an external service (GitHub, GitLab).


The full sequence, as a flow

sequenceDiagram
    participant Tu as You
    participant Gitea as Gitea (namespace gitea)
    participant ArgoCD as ArgoCD (namespace argocd)
    participant K8s as andes-cargo-cluster (namespace andes-cargo)

    Tu->>Gitea: git push (deployment.yaml changed)

    loop every few seconds
        ArgoCD->>Gitea: poll the andes-cargo-k8s repository
        Gitea-->>ArgoCD: latest commit on main
    end

    ArgoCD->>ArgoCD: compares Git (desired) vs cluster (real)

    alt there's a difference
        ArgoCD->>K8s: applies the state declared in Git
        K8s-->>ArgoCD: reconciled state
    else no difference
        ArgoCD->>ArgoCD: Sync Status stays "Synced"
    end

The step with no equivalent in apply.yml: the loop every few seconds. apply.yml has no loop at all — it runs once, when GitHub tells it there was a push, and finishes. ArgoCD never "finishes": the argocd-application-controller Pod (you're going to see it running for real in lesson 4) stays comparing, indefinitely, for as long as the cluster exists.


Analogy: the thermostat that reads the notebook, not the person who calls the heater

Picture two ways of keeping a room at the right temperature. In the first, every time it's cold, you yourself call the heater and ask it to turn on — it works, but it requires you to be present and paying attention all the time. In the second, there's a thermostat on the wall: every few seconds it reads a target temperature you wrote down in a notebook, compares it against the room's actual temperature, and turns the heater on or off on its own, with no intervention from you — you don't even need to be in the house. You're still the one who decides the target temperature (by writing it in the notebook); what the thermostat eliminates is the need for you to execute the correction every time.

apply.yml is the first way: every push to main is, literally, the call to the heater. ArgoCD is the thermostat: the "notebook" is the andes-cargo-k8s repository in Gitea, the "actual temperature" is andes-cargo-cluster's current state, and the comparison cycle runs on its own, every few seconds, for as long as ArgoCD's Pod is alive.


What does NOT change from apply.yml: GitOps' four properties

cicd-and-gitops-on-aws-guide M7.4 already established this table for its own case. It's worth rebuilding it with this guide's names, because the point is the same: push and pull are two implementations of the same principle, neither is "more complete GitOps" than the other.

GitOps propertyHow apply.yml fulfills it (cicd-and-gitops-on-aws-guide)How ArgoCD fulfills it (this module)
Declared desired stateTerraform HCLKubernetes manifests (deployment.yaml, etc.)
Git as the single source of truthpush to main, reviewed before mergingSame — push to main on Gitea's repository
Automatic application of changesapply.yml, triggered by the push eventArgoCD, triggered by its own internal timer
Continuous reconciliationdrift.yml, scheduled, detects and alertsselfHeal: true, continuous, detects and corrects

The last row is, again, the most concrete difference: cicd-and-gitops-on-aws-guide's drift.yml runs on a schedule and alerts if something changed — the fix requires a person to review and approve a new apply. ArgoCD's selfHeal: true, which you're going to see in action with real evidence in lesson 5, corrects on its own, with no one approving anything, the exact moment it detects the difference.


The security implication, the same one you already know

cicd-and-gitops-on-aws-guide M7.4 built this table to explain why the Kubernetes industry prefers pull. It applies unchanged to this module:

Push (apply.yml)Pull (ArgoCD, this module)
Who has write credentials toward the destination?The CI pipeline (external to AWS)No one external — ArgoCD already lives inside andes-cargo-cluster
Where do those credentials live?A GitHub Actions secret, injected on every runNo "external" credentials exist — ArgoCD uses the ServiceAccount it already runs with
If the system that initiates the change gets compromisedAn attacker with access to the pipeline can write to AWS directlyDoesn't apply — there are no write credentials heading outward to steal

The concrete difference in this lab: when you install ArgoCD in lesson 4, you're not going to configure any AWS credential, any access key, nothing that "leaves" the cluster toward an external destination — because the destination is the cluster ArgoCD already runs on. The only credential you are going to configure is a read one, from ArgoCD to Gitea's repository (and, in this lab, you don't even need that: the repository is public inside the cluster, as you're going to confirm in lesson 3).


Common mistakes

Thinking "every few seconds" means "instant" (expectation, clashes with lesson 8). What happens: someone, in lesson 8, does git push and expects to see the change reflected the same second, as if it were push-based. How to spot it: if you get alarmed seeing that kubectl get pods still shows the old state ten seconds after your git push. How to fix it: ArgoCD polls the repository at an interval (a few minutes by default; configurable) — it isn't instant by design, though it does support webhooks to reduce that latency when needed. Lesson 8 measures, with real timestamps, exactly how long it takes in this lab.

Confusing "the repository lives inside the cluster" with "ArgoCD doesn't need any permission" (the same mistake cicd-and-gitops-on-aws-guide M7.4 already warned about, here with even more reason because everything is on the same cluster). What happens: someone concludes that, since everything runs on kind, no kind of access needs to be configured. How to spot it: if you're surprised, in lesson 4, to see ArgoCD get installed with its own set of ServiceAccount/ClusterRole. How to fix it: ArgoCD still needs explicit Kubernetes permissions (RBAC) to create, update, and delete resources inside the namespaces it manages — "living inside the cluster" eliminates the need for external AWS or other-provider credentials, not the need for Kubernetes permissions in general.

Believing this mechanism is exclusive to ArgoCD, and that "GitOps" and "ArgoCD" are synonyms (imprecise vocabulary). What happens: someone starts using "doing GitOps" and "using ArgoCD" as if they were the same phrase. Why it happens: ArgoCD is, today, the market's most cited tool for this mechanism (the evidence from the VALIDACION.md that opened this guide confirms it). How to spot it: if you can't name any other tool that implements the same mechanism. How to fix it: Flux (named in cicd-and-gitops-on-aws-guide M7.3) implements exactly the same pull-based principle, in a different shape (GitRepository + Kustomization instead of Application). GitOps is the principle (Git as the source of truth, with continuous reconciliation); ArgoCD is one tool that implements it, not the only one.


Exercises

Exercise 1 — Draw the credentials diagram, unassisted. Without looking at this lesson, draw (in text) the two credential arrows: where do they travel in apply.yml, and where in ArgoCD?

See solution

apply.yml (push): credentials travel from the GitHub Actions runner (external to AWS) toward AWS — the pipeline needs to authenticate to be able to write there. ArgoCD (pull): there are no write credentials crossing any boundary — ArgoCD already runs inside andes-cargo-cluster, the same cluster it manages, so it uses the ServiceAccount it's already authenticated with locally. The only credential that crosses a boundary is a read one, from ArgoCD to Gitea — and in this lab, not even that, because the repository is public inside the cluster.

Exercise 2 — Apply the distinction to a new scenario. A colleague proposes: "instead of installing ArgoCD, let's have a Kubernetes CronJob run kubectl apply -f against the repository every five minutes." Is that design push or pull? Justify it with this lesson's criterion (who initiates the movement, not where the destination lives).

See solution

It's, in spirit, pull-based — though more rudimentary than ArgoCD. This lesson's criterion isn't "is the destination Kubernetes?", it's "who initiates the movement, and from where?" A CronJob that runs inside the cluster itself, on an internal timer, and goes to fetch the desired state from a Git repository, fulfills the same shape as ArgoCD: the cluster itself initiates the comparison, with no external event triggering it. Compared to ArgoCD, it would be missing concrete things that do matter in production: fine-grained declarative comparison (not just "reapply everything"), real selfHeal, a status UI, safe prune of deleted resources — that's why the industry doesn't reinvent this with a homemade CronJob, but the basic shape of the mechanism (pull, not push) is the same.

Exercise 3 — Explain why Gitea, ArgoCD, and andes-cargo-cluster "being the same cluster" isn't a lab simplification. In two or three sentences, explain to a colleague why this architectural detail of this module is, in fact, more representative of a real EKS than cicd-and-gitops-on-aws-guide's AWS example.

See solution

A reasonable explanation: "In a real production EKS, ArgoCD also runs as Pods inside the very cluster it manages — that doesn't change between kind and EKS, it's the standard way to install ArgoCD anywhere. What usually does change is where the Git repository lives: in production it's normally an external service like GitHub, while this lab brings it inside too (Gitea, inside the same cluster) so it doesn't depend on any external account, at $0. The part that matters for understanding the mechanism — the operator lives inside what it manages — is identical in both cases."


Summary and next step

This lesson made concrete the mechanism the module's introduction named: an operator that lives inside the cluster, compares the real state against a Git repository every few seconds, and corrects the difference with no external event triggering it — the exact opposite of apply.yml, which reacts to a push from outside. You confirmed that GitOps' four properties are fulfilled by both mechanisms, and that pull's security advantage (no write credential travels toward the cluster from outside) has a real cost: polling latency instead of instant reaction, and the need for a Kubernetes cluster running so the operator has somewhere to live.

Before moving on you should be able to: draw this lesson's sequence diagram from memory; explain the difference between drift.yml (detects and alerts) and selfHeal: true (detects and corrects); and defend why Gitea, ArgoCD, and andes-cargo-cluster living on the same kind isn't an artificial lab simplification.

Next lesson: hands-on, Gitea in the cluster. There you install this mechanism's first real piece — the Git repository ArgoCD is going to watch, running inside your own cluster, with the project's official Helm chart.

Resources

  1. cicd-and-gitops-on-aws-guide (NIEVA), Module 7, lesson 4 — the full source for this lesson's diagram and tables, here adapted to Kubernetes actually running.
  2. Argo CD — How it works — official documentation for ArgoCD's continuous reconciliation mechanism.
  3. Weaveworks Blog — What Is GitOps, Really? — the origin of the term GitOps, already cited in cicd-and-gitops-on-aws-guide, the basis for this lesson's four properties.
  4. Gitea — Documentation — the Git server lesson 3 installs inside the cluster.