Skip to main content

Command Palette

Search for a command to run...

GitOps in Action: How Argo CD Image Updater Deploys New Container Images

Updated
•20 min read•View as Markdown
P
Cloud & DevOps Engineer focused on building reliable AWS infrastructure, Kubernetes platforms, GitOps workflows, CI/CD automation, Infrastructure as Code, security, and production-grade observability.

I have been working with Kubernetes, GitHub Actions, Helm, Argo CD and Terraform in my project.

At first, I understood what each tool was doing individually.

But I still had some confusion about the complete flow.

For example:

  • Who actually notices that a new image exists?

  • Does Argo CD pull the image?

  • Where does the new image tag get changed?

  • What exactly is GitOps reconciling?

  • What happens between Argo CD and Kubernetes?

  • What happens when a deployment needs to be rolled back?

  • Why do we even need two Git repositories?

So I decided to break the architecture down from the beginning.

The architecture I am using to understand this article is:

Application Repository → CI/CD → ECR → Image Updater → GitOps Repository → Argo CD → EKS → Pods

Note: My hands-on Kubernetes work has been primarily through a local Kind environment. I am using AWS ECR + EKS here as the cloud architecture I am learning and designing toward, so the concepts are easier to understand in a real AWS environment.

1. First: What Is GitOps?

The simplest way I understand GitOps is:

Git contains the desired state, and a controller continuously works to make the Kubernetes environment match that desired state.

Without GitOps, a deployment might look like:

Developer
    ↓
CI/CD
    ↓
kubectl apply
    ↓
Kubernetes

The CI/CD system directly interacts with the cluster.

With GitOps:

Developer
    ↓
Git
    ↑
Argo CD
    ↓
Kubernetes

The important difference is that Git becomes the source of the desired deployment configuration.

Argo CD watches that configuration and continuously checks whether the cluster matches it.


2. Why Two Git Repositories?

This was one of the things I wanted to understand properly.

In my architecture, there are two repositories.

Repository 1 — Application Repository

This contains things related to the application itself:

Application Repository
├── source code
├── Dockerfile
├── application files
└── CI/CD configuration

This repository answers:

What is the application?


Repository 2 — GitOps Repository

This contains the Kubernetes deployment configuration:

GitOps Repository
├── Helm charts
├── values
├── Kubernetes configuration
└── deployment configuration

This repository answers:

What version/configuration should Kubernetes run?

The application source code does not need to be copied into the GitOps repository.

Instead:

Application Repository
        ↓
      CI/CD
        ↓
   Docker Image
        ↓
       ECR

And the GitOps repository only needs to reference that image:

image:
  repository: <ECR repository>
  tag: 12345

So I think about it as:

Application repo = source of application code

GitOps repo = source of deployment desired state

That separation makes the architecture much easier to manage.


3. Developer Changes the Application

Let's start from the beginning.

Suppose I change something in the application.

I commit and push the change:

Developer
    ↓
Application Repository

The CI/CD pipeline is triggered.

In my architecture, GitHub Actions is responsible for building the application container.


4. CI Builds the Container Image

The pipeline builds a Docker image.

For example:

frontend:12345

The exact tag can be a Git SHA, build number, release version, or another identifier.

I prefer understanding image tags as version identifiers, rather than thinking of an image as simply "the latest one."

The pipeline then pushes the image to Amazon ECR.

Application Repository
        ↓
GitHub Actions
        ↓
Docker Build
        ↓
AWS ECR

At this point, the image exists in the registry.

But Kubernetes is not running it yet.

This is an important distinction.


5. ECR Stores the Image

Amazon ECR is the container registry.

It stores images such as:

frontend:12345
frontend:ABCDEF
frontend:98765

ECR's job is basically:

Store and serve container images.

EKS has a different job.

EKS provides the managed Kubernetes environment where my workloads run.

So:

ECR = stores images

EKS = runs Kubernetes workloads

They are connected, but they are not the same thing.


6. So Who Notices the New Image?

This was one of my biggest points of confusion.

Suppose ECR now contains:

frontend:12345

but the GitOps repository still says:

image:
  repository: <ECR repository>
  tag: 12344

Argo CD is watching Git.

Git still says:

12344

So Argo CD has no reason to deploy 12345.

This is where Argo CD Image Updater comes in.


7. What Does Argo CD Image Updater Actually Do?

Image Updater has a different responsibility from Argo CD.

It can:

  • watch configured container registries

  • find available image tags

  • evaluate the configured update policy

  • determine whether an image should be updated

  • update the configured image reference

So the simplified flow is:

ECR
 ↓
Image Updater
 ↓
Find candidate image
 ↓
Check update policy
 ↓
Update deployment configuration

One important correction to a common misunderstanding:

Image Updater is not responsible for Argo CD's desired-state-vs-live-state reconciliation.

That is Argo CD's job.

I think about the two components like this:

Component Main responsibility
Image Updater Find/select a newer eligible image and update the image reference
Argo CD Reconcile Git desired state with Kubernetes live state

This distinction becomes very important when troubleshooting.


8. A New Image Existing Does Not Mean It Should Be Deployed

Suppose ECR contains:

12344
12345
12346
ABCDEF

Image Updater shouldn't simply say:

"There are new tags, let's deploy everything."

It uses an image update policy.

The policy determines which images/tags are eligible and how Image Updater should select one.

Depending on the configuration, strategies can include things such as:

  • semver-based selection

  • newest-build selection

  • alphabetical/numerical ordering where appropriate

  • tag filters

  • allowed tag patterns

  • other constraints

For example, if I only want versions matching:

v1.x.x

then an image with:

v2.0.0

may not be eligible.

So there is an important difference between:

A new image exists

and:

A new image exists that my update policy allows me to deploy.


9. Git Write-Back

Now we get to one of the most important parts.

Once Image Updater decides that a new image should be used, how does that change become part of GitOps?

There are different write-back methods.

One approach is to update the Helm values directly.

For example:

image:
  repository: <ECR repository>
  tag: 12345

Another approach is to use Argo CD source parameters through a file such as:

.argocd-source-<application-name>.yaml

The exact behavior depends on the Image Updater configuration and write-back method.

This file is not something that Image Updater always creates.

It is used when the Git write-back configuration is set to use the Argo CD source-parameter mechanism.

Conceptually, it allows Image Updater to persist an override for the Argo CD application's source parameters rather than modifying the original Helm values file directly.

So the important idea is:

Image Updater
      ↓
Git write-back
      ↓
GitOps repository
      ↓
Git commit

Git is still involved.

That's the part I initially didn't fully understand.


10. Why Does Git Write-Back Matter?

Imagine Image Updater simply changed Kubernetes directly.

Then we could have:

Git:
12344

Kubernetes:
12345

Now the declared configuration and actual cluster state don't tell the same story.

With Git write-back:

ECR
 ↓
Image Updater
 ↓
GitOps Repository
 ↓
Git Commit
 ↓
Argo CD
 ↓
Kubernetes

The deployment change becomes visible in Git.

That gives us:

  • history

  • traceability

  • reviewability

  • auditability

  • rollback possibilities

It also keeps the GitOps workflow consistent.


11. Argo CD Takes Over

Once the GitOps repository changes, Argo CD detects the change.

Now Argo CD performs its actual GitOps job.

It determines:

What does Git say should exist?

That is the desired state.

Then Argo CD gets information about the running Kubernetes resources through the Kubernetes API.

That is the live/actual state.

For example:

Desired state:
frontend:12345

Live state:
frontend:12344

There is a difference.

Argo CD identifies that difference and reconciles the application.


12. What Does "Reconciliation" Actually Mean?

This word gets used everywhere in Kubernetes, so I wanted to understand it rather than just memorize it.

The basic idea is:

Observe
   ↓
Compare
   ↓
Find difference
   ↓
Take corrective action
   ↓
Observe again

For example:

Git:
12345

Kubernetes:
12344

Argo CD detects the difference.

It applies the desired configuration.

Eventually:

Git:
12345

Kubernetes:
12345

The two states have converged.

That's reconciliation.


13. Argo CD Does Not "Send the Image to EKS"

Another important distinction.

Argo CD does not download the application image from ECR and send it to EKS.

Argo CD works with Kubernetes resources.

For example, it can apply a Deployment that references:

image: <ECR repository>:12345

Then Kubernetes handles the workload.

The simplified chain is:

Argo CD
   ↓
Kubernetes API
   ↓
Deployment
   ↓
ReplicaSet
   ↓
Pod

And when the Pod needs the container image:

Pod
 ↓
Container runtime
 ↓
ECR
 ↓
Pull image

So there are two separate flows:

Configuration flow

Git
 ↓
Argo CD
 ↓
Kubernetes API
 ↓
Deployment

Image flow

ECR
 ↓
Container runtime
 ↓
Pod

This distinction is extremely important.


14. Where Does EKS Fit?

EKS is managed Kubernetes.

At a high level:

Argo CD
    ↓
Kubernetes API Server
    ↓
EKS Control Plane
    ↓
Kubernetes Controllers / Scheduler
    ↓
Worker Nodes
    ↓
Pods

The Kubernetes API server is the interface Argo CD communicates with.

EKS manages the Kubernetes control plane for the cluster.

The worker nodes are where the application Pods actually run.

So saying:

"Argo CD sends the deployment to EKS"

is a little too vague.

A better explanation is:

Argo CD communicates with the Kubernetes API server of the EKS cluster and applies the desired Kubernetes resources.


15. How Does the Pod Get the Image from ECR?

Suppose Kubernetes now needs:

frontend:12345

The Pod is scheduled onto a worker node.

The container runtime on that node needs to pull the image from ECR.

That requires appropriate AWS permissions.

The exact mechanism depends on the EKS setup, but the important idea is:

The workload/node needs permission to retrieve the image from ECR.

Argo CD doesn't need to pull that image itself.

So:

Argo CD
   │
   │ Kubernetes configuration
   ▼
EKS / Kubernetes
   │
   │ image reference
   ▼
Pod
   │
   │ image pull
   ▼
ECR

This is another place where I initially mixed responsibilities between components.


16. What Happens During a Rolling Update?

Now suppose the old image is:

12344

and the new image is:

12345

Argo CD applies the updated Deployment.

The Deployment's Pod template has changed.

With the normal RollingUpdate strategy, Kubernetes creates a new ReplicaSet for the new Pod template.

Conceptually:

Deployment
   │
   ├── Old ReplicaSet → Pods running 12344
   │
   └── New ReplicaSet → Pods running 12345

Kubernetes gradually creates new Pods and removes old Pods according to the Deployment's rollout configuration.

Readiness probes and rollout settings help determine when new Pods are ready.

Eventually:

Old Pods → terminated

New Pods → running 12345

For stateless workloads, this replacement model works very well.


17. Stateless vs Stateful

This is another distinction worth understanding.

A stateless application should not depend on data stored only inside an individual Pod.

If the Pod disappears, another Pod should be able to start without losing important application state.

Durable state should normally live somewhere designed for persistence, such as:

  • a database

  • persistent storage

  • an external storage service

For stateful workloads, Kubernetes has mechanisms such as:

  • PersistentVolume

  • PersistentVolumeClaim

  • StatefulSet

depending on the architecture.

The important distinction isn't:

"Stateful means sensitive."

It is:

Does the workload depend on persistent state, and where is that state stored?


18. What If I Change the GitOps Repository Directly?

Image Updater is not required for every GitOps change.

This is important.

Suppose I manually change:

replicas: 3

to:

replicas: 5

in the GitOps repository.

Or I change:

  • CPU/memory

  • Service configuration

  • Ingress

  • environment variables

  • Helm values

  • Kubernetes manifests

Then the flow is simply:

GitOps Repository
       ↓
     Argo CD
       ↓
Kubernetes API
       ↓
      EKS

Image Updater isn't involved.

So there are two different paths.

Path A — Application image update

Application Repo
      ↓
     CI/CD
      ↓
     ECR
      ↓
Image Updater
      ↓
GitOps Repo
      ↓
    Argo CD
      ↓
      EKS

Path B — Direct GitOps configuration change

GitOps Repo
      ↓
    Argo CD
      ↓
      EKS

Both eventually reach Argo CD reconciliation.

Only the image-update path requires Image Updater.


19. Now the Important Part: Rollback

This is one of the reasons I like the GitOps model.

Suppose Git initially contained:

Commit A
image = 12345

Then Image Updater updates it:

Commit B
image = ABCDEF

Later we discover that ABCDEF has a problem.

The first thing to understand is:

Commit A did not disappear.

Git history still contains it.

So we have:

A: image = 12345
        ↓
B: image = ABCDEF

We can create another commit that reverses B.

git revert <commit-B>

Now the history becomes:

A: image = 12345
        ↓
B: image = ABCDEF
        ↓
C: revert B → image = 12345

This is useful because we haven't erased the previous history.

Argo CD sees the new desired state:

image = 12345

and reconciles Kubernetes back to that image.


20. Git Revert vs Git Reset

I initially had to separate these two concepts.

git reset

Reset moves the branch's HEAD.

Depending on how it is used, it can also change the staging area and working tree.

If used on already-published shared history and followed by force-pushing, it can rewrite history.

git revert

Revert creates a new commit that reverses an earlier commit.

For example:

A → B → C

If C reverts B:

A → B → C

but the resulting content is back to what B changed from.

For a shared GitOps repository, git revert is generally the safer approach for undoing an already-published change because the history remains intact.

That gives us:

  • auditability

  • traceability

  • clear change history

  • easier investigation

  • safer collaboration


21. GitOps Rollback Does Not Roll Back Application Source Code

This is another important point.

Remember that we have two repositories.

Suppose:

Application Repository
Version 10

and:

GitOps Repository
image = ABCDEF

Now I revert the GitOps deployment to:

image = 12345

I have not rolled back the application repository.

The application repository still contains whatever code is currently there.

I have only changed:

Which already-built container image Kubernetes should run.

So there is a difference between:

Application code rollback

Reverting application source code.

Deployment rollback

Changing the deployment to run an earlier container image.

GitOps makes the second one particularly straightforward when previous image references still exist.


22. What If the Old Image Was Deleted?

There is a catch.

Suppose I successfully roll the GitOps repository back to:

image = 12345

But someone already deleted 12345 from ECR.

Now Kubernetes can't pull it.

The GitOps configuration can be perfectly correct while the deployment still fails.

This is why image retention is part of a real rollback strategy.

A production system should consider:

  • image lifecycle policies

  • retention periods

  • immutable image references

  • rollback requirements

  • storage costs

A rollback is only useful if the artifact being rolled back to still exists.


23. Why Immutable Image Tags Matter

This is another lesson from the architecture.

Using:

latest

can create ambiguity.

If latest points to a different image tomorrow, the same tag no longer necessarily represents the same artifact.

Instead, we can use immutable identifiers such as:

8f3a91c

where the value represents a Git commit or build identifier.

For example:

Git commit:
8f3a91c

Docker image:
my-app:8f3a91c

GitOps:
image: my-app:8f3a91c

Now I can clearly answer:

Which exact application build is running?

This improves:

  • traceability

  • reproducibility

  • debugging

  • rollback

  • auditing

The exact tagging strategy depends on how the CI/CD pipeline is designed.


24. Common Misconceptions

These were some of the biggest things I had to clear up.

1. "Image Updater performs reconciliation."

No.

Argo CD performs GitOps reconciliation.

Image Updater handles image discovery, selection and image-reference updates.


2. "Argo CD watches my application source code."

Not necessarily.

Argo CD watches the configured GitOps source containing the deployment configuration.


3. "The GitOps repository contains my application."

Not necessarily.

The application source can remain in a separate repository.

The GitOps repository references the container image that should be deployed.


4. "ECR runs my containers."

No.

ECR stores container images.

EKS runs Kubernetes workloads.


5. "Argo CD sends the image to EKS."

No.

Argo CD applies Kubernetes configuration.

The container runtime pulls the image from ECR when a Pod needs it.


6. "EKS is just a server."

No.

EKS is a managed Kubernetes service, including the managed Kubernetes control plane.


7. "When a GitOps file changes, the old version is gone."

No.

The current file changes, but Git history still contains previous commits.


8. "Use reset to undo a production GitOps commit."

Usually not for a shared published change.

git revert is generally preferred because it preserves history.


9. "Rolling back GitOps rolls back application source code."

No.

It changes which deployment state/image is desired.


10. "Image Updater is needed for every GitOps change."

No.

Direct changes to the GitOps repository can go directly through Argo CD.


25. The Complete Picture

Now the whole architecture makes much more sense to me.

                 APPLICATION SIDE

Developer
    │
    ▼
Application Git Repository
    │
    ▼
GitHub Actions
    │
    ▼
Docker Build
    │
    ▼
AWS ECR
    │
    │
    ▼
Argo CD Image Updater
    │
    │ Git write-back
    ▼
GitOps Repository
    │
    ▼
Argo CD
    │
    │ reconciliation
    ▼
Kubernetes API
    │
    ▼
Amazon EKS
    │
    ▼
Deployment
    │
    ▼
ReplicaSet
    │
    ▼
Pods
    │
    │ image pull
    ▼
AWS ECR

The key thing is that there isn't one single component doing everything.

Each part has a job:

Component What it does
Application Git Stores application source
GitHub Actions Builds the application image
ECR Stores the container image
Image Updater Finds/selects eligible newer images and updates the image reference
GitOps Git Stores deployment desired state
Argo CD Reconciles desired state with Kubernetes
Kubernetes API Interface through which resources are managed
EKS Managed Kubernetes environment
Deployment Manages the application rollout
ReplicaSet Maintains the desired number of Pods
Pod Runs the application container

What I Actually Learned

The biggest thing I learned wasn't another command.

It was understanding where responsibility belongs.

When I see a new image, I should think:

Did CI build it?
        ↓
Does ECR have it?
        ↓
Is it eligible under Image Updater policy?
        ↓
Was the image reference written to Git?
        ↓
Did Argo CD detect the Git change?
        ↓
Did Argo CD reconcile successfully?
        ↓
Did Kubernetes create the new Pods?
        ↓
Can the Pods pull the image?
        ↓
Are the Pods actually healthy?

That gives me a much better way to troubleshoot the system.

Instead of saying:

"Argo CD deployment isn't working."

I can ask:

Which part of the chain actually failed?

And that is the mental model I wanted to build before moving on to the next topic.


A Few Interview Questions I Can Now Answer

Beginner

What is GitOps?

GitOps is a deployment model where Git represents the desired state of the environment and a controller such as Argo CD continuously reconciles that desired state with the Kubernetes cluster.

Why use two repositories?

The application repository manages source code and CI/CD, while the GitOps repository manages deployment configuration and desired state.

What is ECR?

A managed AWS container registry used to store container images.

What is EKS?

A managed AWS Kubernetes service used to run Kubernetes workloads.


Intermediate

What is the difference between Argo CD and Image Updater?

Image Updater finds and selects eligible newer images and updates the image reference. Argo CD reconciles the desired state from Git with the live Kubernetes state.

What is Git write-back?

It is the process of persisting the image update into the GitOps repository so that the image change becomes part of the Git-managed desired state.

What happens after Argo CD detects a Git change?

Argo CD calculates the desired state, compares it with the live state obtained through the Kubernetes API, and reconciles the difference.


Advanced

How would you roll back a broken deployment?

I would normally revert the GitOps change using git revert, allowing Argo CD to reconcile the previous desired image back into Kubernetes, assuming the previous image still exists in the registry.

Why prefer immutable image tags?

They make deployments traceable and reproducible and make it possible to identify and roll back to a specific artifact.

What happens if the old image was deleted from ECR?

The GitOps rollback can still point to the old tag, but Kubernetes won't be able to pull the missing image. This is why image retention is part of a rollback strategy.


Conclusion

Before working on this architecture, I knew the names:

Kubernetes. Argo CD. Helm. Docker. GitHub Actions. ECR. EKS.

But knowing the tools separately is different from understanding how they work together.

The mental model I am taking from this is:

Application Code
      ↓
Application Git
      ↓
CI/CD
      ↓
Container Image
      ↓
ECR
      ↓
Image Updater
      ↓
GitOps Git
      ↓
Argo CD
      ↓
Kubernetes API
      ↓
EKS
      ↓
Deployment
      ↓
Pods
      ↓
Image pulled from ECR

And if something breaks, I now have a chain to investigate instead of treating the entire platform as one black box.

That's the part I wanted to understand.


What I'll cover next

There are still a lot of topics I want to break down separately:

  • Kubernetes internals

  • Helm

  • Argo CD deeper

  • Vault

  • AWS infrastructure with Terraform

  • CI/CD

  • Observability

  • Security

  • Troubleshooting

I'll keep them as separate articles instead of putting everything into one huge post.

24 views