Helm Image Tags Annotations

When executing the Update Argo CD Application Image Tags step against an Argo CD Application that is deploying a Helm chart, it is necessary to provide extra annotations to define which fields in the Helm values file represent an image to be updated.

This is because an image reference could be made up of multiple different values file entries. Consider the image fields in values.yaml for the Kubernetes agent Helm chart:

...
agent:
  image:
    repository: octopusdeploy/kubernetes-agent-tentacle
    pullPolicy: IfNotPresent
    tag: "8.3.3244"
    tagSuffix: ""
...

In this case, the agent.image.tag contains the tag of the image to be updated. However, consider the image fields in this example:

...
global:
  image:
    registry: docker.io
    repositoryAndTag: octopusdeploy/kubernetes-agent-tentacle:8.3.3244
...

In this case, the global.image.repositoryAndTag contains the tag to be updated.

As the structure of Helm values files can vary widely between charts, it’s necessary to require you to specify custom annotations on the Argo CD Applications.

The annotations are:

AnnotationAlias requiredRequiredValue description
argo.octopus.com/image-replace-paths.{alias}falsetrueA comma-delimited Helm-template style string that builds a list of full qualified image names
argo.octopus.com/image-replace-alias.{alias}truefalseThe path of a ValueFiles entry in the spec.destinations.helm.valuesFiles field

Details

For Octopus to be able to update the tag of a container image, it must know the fully qualified name, including the registry. An example of a fully qualified name is: docker.io/nginx/nginx:1.29.1. This is important so that Octopus doesn’t erroneously update an image from a different registry. For example: images may be set to be sourced from a company-managed registry, where only vetted & tested tags are added. In this case, we don’t want to update an image that looks like this: my-company-registry.com/nginx/nginx:1.18.1.

As described above however, the structure of Helm values files can vary significantly. Rather than Octopus guessing (and possibly making a mistake), the onus is on you to specify a Helm-template string that builds a fully qualified name. Octopus can then use this to match on containers being updated and can then use this information to update the specific Helm value that contains the image tag.

Examples

Image path templates

The following is some examples of how to format the Helm-templated string to put into the argo.octopus.com/image-replace-paths.{alias} annotation based on different values file structures.

Example 1

values.yaml

...
agent:
  image:
    repository: octopusdeploy/kubernetes-agent-tentacle
    pullPolicy: IfNotPresent
    tag: "8.3.3244"
    tagSuffix: ""
...

annotation value

metadata:
  annotations:
    argo.octopus.com/image-replace-paths: "docker.io/{{ .Values.agent.image.repository }}:{{ .Values.agent.image.tag }}"

Example 2

values.yaml

...
global:
  image:
    registry: custom-registry.com
    repositoryAndTag: octopusdeploy/kubernetes-agent-tentacle:8.3.3244
    pullPolicy: IfNotPresent
    tagSuffix: ""
...

annotation value

metadata:
  annotations:
    argo.octopus.com/image-replace-paths: "{{ .Values.global.image.registry }}/{{ .Values.global.image.repositoryAndTag }}"

Ref sources and alias examples

The following is a list of example Argo CD Application structures, the required annotations and sample values files.

Example 1

With a single values file and a single Helm source, we don’t need the alias in the paths.

application manifest

...
metadata:
  annotations:
    argo.octopus.com/project: "proj-1"
    argo.octopus.environment: "development"

    # When there is a single source, with a single inline file, we use a single annotation to specify call paths
    argo.octopus.com/image-replace-paths: "{{ .Values.image.name}}:{{ .Values.image.version}}, {{ .Values.another-image.name }}"
...
spec:
  sources:    
    - repoURL: https://github.com/my-org/my-argo-helm-app
      path: "chart"
      targetRevision: main
      helm:
        valueFiles:
          - values.yaml

values.yaml

image:
  name: nginx/nginx
  version: 1.19.0

another-image:
  name: busybox:1

Example 2

A single Ref source used to source the values.yaml for the Helm source. In this scenario, the alias is the same as the ref value.

...
metadata:
  annotations:
    argo.octopus.com/project: "proj-1"
    argo.octopus.environment: "development"

    argo.octopus.com/image-replace-paths.remote-values: "{{ .Values.image.name}}:{{ .Values.image.version}}, {{ .Values.another-image.name }}"
...
spec:
  sources:    
    - repoURL: https://github.com/my-org/my-argo-helm-app
      path: "chart"
      targetRevision: main
      helm:
        valueFiles:
          - $remote-values/values.yaml

    - repoURL: https://github.com/another-repo/values-files-here
      targetRevision: main
      ref: remote-values

values.yaml

image:
  name: nginx/nginx
  version: 1.19.0

another-image:
  name: busybox:1

Example 3

A Helm source that references both a ref sourced values file and also an in-repo value file.

...
metadata:
  annotations:
    argo.octopus.com/project: "proj-1"
    argo.octopus.environment: "development"

    argo.octopus.com/image-replace-alias.core: "app-files/values.yaml"
    argo.octopus.com/image-replace-alias.remote: "$remote-values/values.yaml"

    argo.octopus.com/image-replace-paths.core: "{{ .Values.image.name}}:{{ .Values.image.version}}"
    argo.octopus.com/image-replace-paths.remote: "{{ .Values.different.structure.here.image }}"
...
spec:
  sources:    
    - repoURL: https://github.com/my-org/my-argo-helm-app
      path: "chart"
      targetRevision: main
      helm:
        valueFiles:
          - app-files/values.yaml
          - $remote-values/values.yaml

    - repoURL: https://github.com/another-repo/values-files-here
      targetRevision: main
      ref: remote-values

app-files/values.yaml

image:
  name: nginx/nginx
  version: 1.19.0

$remote-values/values.yaml

different:
  structure:
    here:
      image: busybox:1

Example 4

A Helm source that references multiple values files from the source repo.

...
metadata:
  annotations:
    argo.octopus.com/project: "proj-1"
    argo.octopus.environment: "development"

    # When there are multiple sources, we need an annotation to tell us which file path annotations belong to (Note: the actual name of the alias can be arbitrary)
    # if an alias is not provided for a path, that values file will be ignored
    argo.octopus.com/image-replace-alias.core: "app-files/values.yaml"
    argo.octopus.com/image-replace-alias.overlay: "app-files/values-overlay.yaml"

    argo.octopus.com/image-replace-paths.core: "{{ .Values.image.name}}:{{ .Values.image.version}}"
    argo.octopus.com/image-replace-paths.overlay: "{{ .Values.different.structure.here.image }}"
...
spec:
  sources:    
    - repoURL: https://github.com/my-org/my-argo-helm-app
      path: "chart"
      targetRevision: main
      helm:
        valueFiles:
          - app-files/values.yaml
          - app-files/values-overlay.yaml

app-files/values.yaml

image:
  name: nginx/nginx
  version: 1.19.0

$remote-values/values-overlay.yaml

different:
  structure:
    here:
      image: busybox:1

Example 5

A Helm source that has multiple ref sourced values files.

...
metadata:
  annotations:
    argo.octopus.com/project: "proj-1"
    argo.octopus.environment: "development"

    argo.octopus.com/image-replace-paths.other-values: "{{ .Values.another-image.name }}"
    argo.octopus.com/image-replace-paths.remote-values: "{{ .Values.image.name}}:{{ .Values.image.version}}"
...
spec:
  sources:
    - repoURL: https://github.com/main-repo/values-files-here
      targetRevision: main
      ref: other-values

    - repoURL: https://github.com/another-repo/values-files-here
      targetRevision: main
      ref: remote-values

    - repoURL: https://github.com/my-repo/my-argo-app
      path: "./"
      targetRevision: main
      helm:
        valueFiles:
          - $other-values/values.yaml
          - $remote-values/values.yaml

$remote-values/values.yaml

image:
  name: nginx/nginx
  version: 1.19.0

$other-values/values.yaml

another-image:
  name: busybox:1

Example 6

Multiple Helm sources with a same values file path in both sources.

Note: The alias requires a fully qualified repo path in the format: {repoUrl}/{targetRevision}/{path}/{valuesFile}.

...
metadata:
  annotations:
    argo.octopus.com/project: "proj-1"
    argo.octopus.environment: "development"

    argo.octopus.com/image-replace-alias.app1: "https://github.com/my-repo/my-argo-app/main/values.yaml"
    argo.octopus.com/image-replace-alias.app2: "https://github.com/my-repo/my-other-argo-app/main/cool/values.yaml"

    argo.octopus.com/image-replace-paths.app1: "{{ .Values.image.name}}:{{ .Values.image.version}}"
    argo.octopus.com/image-replace-paths.app2: "{{ .Values.different.structure.here.image }}"
...
spec:
  sources:
    - repoURL: https://github.com/my-repo/my-argo-app
      path: "./"
      targetRevision: main
      helm:
        valueFiles:
          - values.yaml

    - repoURL: https://github.com/my-repo/my-other-argo-app
      path: "cool"
      targetRevision: main
      helm:
        valueFiles:
          - values.yaml

https://github.com/my-repo/my-argo-app/main/values.yaml

image:
  name: nginx/nginx
  version: 1.19.0

https://github.com/my-repo/my-other-argo-app/main/cool/values.yaml

different:
  structure:
    here:
      image: busybox:1

Example 7

Multiple Helm sources that reference different values files from the same ref source.

...
metadata:
  annotations:
    argo.octopus.com/project: "proj-1"
    argo.octopus.environment: "development"

    argo.octopus.com/image-replace-alias.shared1: "$shared-values/some-path/values.yaml"
    argo.octopus.com/image-replace-alias.shared2: "$shared-values/another-path/values.yaml"

    argo.octopus.com/image-replace-paths.shared1: "{{ .Values.image.name}}:{{ .Values.image.version}}"
    argo.octopus.com/image-replace-paths.shared2: "{{ .Values.different.structure.here.image }}"
...
spec:
  sources:
    - repoURL: https://github.com/another-repo/shared-values-files-here
      targetRevision: main
      ref: shared-values

    - repoURL: https://github.com/my-repo/my-argo-app-be
      path: "app-files"
      targetRevision: main
      helm:
        valueFiles:
          - $shared-values/some-path/values.yaml

    - repoURL: https://github.com/my-repo/my-argo-app-fe
      path: "app-files"
      targetRevision: main
      helm:
        valueFiles:
          - $shared-values/another-path/values.yaml

$shared-values/some-path/values.yaml

image:
  name: nginx/nginx
  version: 1.19.0

$shared-values/another-path/values.yaml

different:
  structure:
    here:
      image: busybox:1

Help us continuously improve

Please let us know if you have any feedback about this page.

Send feedback

Page updated on Monday, September 15, 2025