Automated Image Updates with Argo CD Image Updater

Watching container registries for new tags and updating Applications automatically - update strategies and Git write-back

Introduction

Pure GitOps means every deployed change traces back to a Git commit - but that leaves a gap for the most common change of all, a new image tag after CI builds one. Someone (or some CI job) still has to open a PR bumping the tag in Git. Argo CD Image Updater is a separate controller (again, its own project alongside core ArgoCD) that closes that gap: it polls configured registries, and when it finds a new tag matching an Application’s update strategy, it updates the image reference itself - either directly against the running Application or by committing back to Git, keeping the automation GitOps-compatible either way.

Installing Image Updater

kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj-labs/argocd-image-updater/stable/manifests/install.yaml

It runs as its own Deployment in the argocd namespace, separate from the core argocd-application-controller, argocd-repo-server, etc. - it needs read access to the Applications it manages and (for the Git write-back method) write access to the Git repos it commits to.

Marking an Application for Updates

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: web-app
  annotations:
    argocd-image-updater.argoproj.io/image-list: webapp=myregistry/web-app
    argocd-image-updater.argoproj.io/webapp.update-strategy: semver
    argocd-image-updater.argoproj.io/webapp.allow-tags: regexp:^[0-9]+\.[0-9]+\.[0-9]+$
spec:
  source:
    repoURL: https://github.com/example/web-app-manifests.git
    targetRevision: main
    path: manifests
    helm:
      parameters:
        - name: image.tag
          value: 2.3.0

image-list declares which image(s) to watch and gives each an alias (webapp here) that the rest of the annotations reference. update-strategy: semver picks the highest semantic version among tags matching allow-tags - other strategies are latest (most recently pushed tag, by registry creation timestamp), digest (track a single mutable tag like latest by digest rather than by a changing tag name), and name (lexically greatest tag string, for non-semver schemes).

Update Methods

By default Image Updater updates the live Application object directly (adding an override parameter ArgoCD then syncs) - fast, but that override lives only in the Application resource, not in Git, so it’s invisible to anyone reading the repo and gets lost if the Application itself is ever recreated from Git. The Git write-back method fixes that by committing the change to the actual source repo:

metadata:
  annotations:
    argocd-image-updater.argoproj.io/write-back-method: git
    argocd-image-updater.argoproj.io/git-branch: main
apiVersion: v1
kind: Secret
metadata:
  name: git-creds
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: repo-creds
stringData:
  url: https://github.com/example/web-app-manifests.git
  username: image-updater-bot
  password: ghp_xxxxxxxxxxxxxxxxxxxx

With write-back-method: git, a detected new tag results in an actual commit to manifests on the main branch, updating the Helm values.yaml (or Kustomize kustomization.yaml image override, depending on the Application’s source type) - the change is now a real Git commit with a real author, restoring the audit trail that direct-to-Application updates skip, at the cost of one extra reconcile round-trip (commit, then ArgoCD’s own Git polling picks it up and syncs).

Registry Authentication

apiVersion: v1
kind: Secret
metadata:
  name: image-updater-registry-creds
  namespace: argocd
stringData:
  registries.conf: |
    registries:
      - name: My Private Registry
        api_url: https://myregistry.example.com
        prefix: myregistry.example.com
        credentials: secret:argocd/image-updater-registry-creds#creds
        credsexpire: 10h

Public registries (Docker Hub, ghcr.io for public images) work without configuration, but a private registry needs credentials configured either per-registry in registries.conf or, on EKS/GKE/AKS, via the node’s own cloud-provider IAM identity if the registry is the cloud’s native one (ECR, Artifact Registry, ACR) and workload identity is already set up.

Kustomize Applications

metadata:
  annotations:
    argocd-image-updater.argoproj.io/image-list: webapp=myregistry/web-app
    argocd-image-updater.argoproj.io/webapp.update-strategy: semver
    argocd-image-updater.argoproj.io/webapp.kustomize.image-name: myregistry/web-app

For a Kustomize-sourced Application (rather than Helm), the update targets kustomization.yaml’s images: block instead of Helm values - kustomize.image-name tells Image Updater which entry in that block corresponds to the aliased image.

Best Practices

  1. Pin allow-tags to a real pattern rather than leaving it unset - without it, semver strategy considers every tag, including ones that aren’t actually semver and get silently skipped, or worse, aren’t your release tags at all (a latest or sha-abc123 tag mixed into the same repository).
  2. Prefer Git write-back over direct-Application updates for anything beyond a dev/sandbox environment - the audit trail and rollback-via-git revert it preserves are usually worth the extra round-trip.
  3. Scope registry and Git credentials to exactly what Image Updater needs - a bot token with write access to unrelated repos, or registry credentials broader than the images actually being watched, is unnecessary blast radius.
  4. Combine with Argo Rollouts for anything beyond dev - Image Updater changing a tag is just a new desired state; pairing it with a Rollout’s canary/analysis strategy (rather than a plain Deployment) means an automated image bump also gets automated safety checks before it reaches 100% traffic.

Conclusion

Image Updater is the piece that makes “new image tag” not require a human to open a PR - the meaningful design choice is write-back method: direct-to-Application is fast feedback for low-stakes environments, Git write-back keeps the full GitOps audit trail for anything that matters.

Additional Resources