github.com/argoproj/argo-cd/v3@v3.2.1/docs/user-guide/source-hydrator.md (about) 1 # Source Hydrator 2 3 **Current feature state**: Alpha 4 5 Tools like Helm and Kustomize allow users to express their Kubernetes manifests in a more concise and reusable way 6 (keeping it DRY - Don't Repeat Yourself). However, these tools can obscure the actual Kubernetes manifests that are 7 applied to the cluster. 8 9 The "rendered manifest pattern" is a feature of Argo CD that allows users to push the hydrated manifests to git before syncing them to the cluster. This 10 allows users to see the actual Kubernetes manifests that are applied to the cluster. 11 12 ## Enabling the Source Hydrator 13 14 The source hydrator is disabled by default. 15 16 To enable the source hydrator, you need to enable the "commit server" component and set the `hydrator.enabled` field in 17 argocd-cmd-params-cm ConfigMap to `"true"`. 18 19 ```yaml 20 apiVersion: v1 21 kind: ConfigMap 22 metadata: 23 name: argocd-cmd-params-cm 24 namespace: argocd 25 data: 26 hydrator.enabled: "true" 27 ``` 28 29 !!! important 30 After updating the ConfigMap, you must restart the Argo CD controller and API server for the changes to take effect. 31 32 If you are using one of the `*-install.yaml` manifests to install Argo CD, you can use the 33 `*-install-with-hydrator.yaml` version of that file instead. 34 35 For example, 36 37 ``` 38 Without hydrator: https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml 39 With hydrator: https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install-with-hydrator.yaml 40 ``` 41 42 !!! important 43 The `*-with-hydrator-install.yaml` manifests will eventually be removed when the source hydrator is either enabled 44 by default or removed. The upgrade guide will note if the `install-with-hydrator.yaml` manifests are no longer 45 available. 46 47 ## Using the Source Hydrator 48 49 To use the source hydrator, you must first install a push and a pull secret. This example uses a GitHub App for authentication, but 50 you can use [any authentication method that Argo CD supports for repository access](../operator-manual/declarative-setup.md#repositories). 51 52 ```yaml 53 apiVersion: v1 54 kind: Secret 55 metadata: 56 name: my-push-secret 57 namespace: argocd 58 labels: 59 argocd.argoproj.io/secret-type: repository-write 60 type: Opaque 61 stringData: 62 url: "https://github.com" 63 type: "git" 64 githubAppID: "<your app ID here>" 65 githubAppInstallationID: "<your installation ID here>" 66 githubAppPrivateKey: | 67 <your private key here> 68 --- 69 apiVersion: v1 70 kind: Secret 71 metadata: 72 name: my-pull-secret 73 namespace: argocd 74 labels: 75 argocd.argoproj.io/secret-type: repository 76 type: Opaque 77 stringData: 78 url: "https://github.com" 79 type: "git" 80 githubAppID: "<your app ID here>" 81 githubAppInstallationID: "<your installation ID here>" 82 githubAppPrivateKey: | 83 <your private key here> 84 ``` 85 86 The only difference between the secrets above, besides the resource name, is that the push secret contains the label 87 `argocd.argoproj.io/secret-type: repository-write`, which causes the Secret to be used for pushing manifests to git 88 instead of pulling from git. Argo CD requires different secrets for pushing and pulling to provide better isolation. 89 90 Once your secrets are installed, set the `spec.sourceHydrator` field of the Application. For example: 91 92 ```yaml 93 apiVersion: argoproj.io/v1alpha1 94 kind: Application 95 metadata: 96 name: my-app 97 spec: 98 sourceHydrator: 99 drySource: 100 repoURL: https://github.com/argoproj/argocd-example-apps 101 path: helm-guestbook 102 targetRevision: HEAD 103 syncSource: 104 targetBranch: environments/dev 105 path: helm-guestbook 106 ``` 107 108 In this example, the hydrated manifests will be pushed to the `environments/dev` branch of the `argocd-example-apps` 109 repository. 110 111 When using source hydration, the `syncSource.path` field is required and must always point to a non-root 112 directory in the repository. Setting the path to the repository root (for eg. `"."` or `""`) is not 113 supported. This ensures that hydration is always scoped to a dedicated subdirectory, which avoids unintentionally overwriting or removing files that may exist in the repository root. 114 115 During each hydration run, Argo CD cleans the application's configured path before writing out newly generated manifests. This guarantees that old or stale files from previous hydration do not linger in the output directory. However, the repository root is never cleaned, so files such as CI/CD configuration, README files, or other root-level assets remain untouched. 116 117 It is important to note that hydration only cleans the currently configured application path. If an application’s path changes, the old directory is not removed automatically. Likewise, if an application is deleted, its output path remains in the repository and must be cleaned up manually by the repository owner if desired. This design is intentional: it prevents accidental deletion of files when applications are restructured or removed, and it protects critical files like CI pipelines that may coexist in the repository. 118 119 !!! important "Project-Scoped Repositories" 120 121 Repository Secrets may contain a `project` field, making the secret only usable by Applications in that project. 122 The source hydrator only supports project-scoped repositories if all Applications writing to the same repository and 123 branch are in the same project. If Applications in different projects write to the same repository and branch, the 124 source hydrator will not be able to use a project-scoped repository secret and will require a global repository 125 secret. This behavior may change in the future. 126 127 If there are multiple repository-write Secrets available for a repo, the source hydrator will non-deterministically 128 select one of the matching Secrets and log a warning saying "Found multiple credentials for repoURL". 129 130 ## Pushing to a "Staging" Branch 131 132 The source hydrator can be used to push hydrated manifests to a "staging" branch instead of the `syncSource` branch. 133 This provides a way to prevent the hydrated manifests from being applied to the cluster until some prerequisite 134 conditions are met (in effect providing a way to handle environment promotion via Pull Requests). 135 136 To use the source hydrator to push to a "staging" branch, set the `spec.sourceHydrator.hydrateTo` field of the 137 Application. For example: 138 139 ```yaml 140 apiVersion: argoproj.io/v1alpha1 141 kind: Application 142 metadata: 143 name: my-app 144 spec: 145 project: my-project 146 destination: 147 server: https://kubernetes.default.svc 148 namespace: default 149 sourceHydrator: 150 drySource: 151 repoURL: https://github.com/argoproj/argocd-example-apps 152 path: helm-guestbook 153 targetRevision: HEAD 154 syncSource: 155 targetBranch: environments/dev 156 path: helm-guestbook 157 hydrateTo: 158 targetBranch: environments/dev-next 159 ``` 160 161 In this example, the hydrated manifests will be pushed to the `environments/dev-next` branch, and Argo CD will not sync 162 the changes until something moves them to the `environments/dev` branch. 163 164 You could use a CI action to move the hydrated manifests from the `hydrateTo` branch to the `syncSource` branch. To 165 introduce a gating mechanism, you could require a Pull Request to be opened to merge the changes from the `hydrateTo` 166 branch to the `syncSource` branch. 167 168 Argo CD will only push changes to the `hydrateTo` branch, it will not create a PR or otherwise facilitate moving those 169 changes to the `syncSource` branch. You will need to use your own tooling to move the changes from the `hydrateTo` 170 branch to the `syncSource` branch. 171 172 ## Commit Tracing 173 174 It's common for CI or other tooling to push DRY manifest changes after a code change. It's important for users to be 175 able to trace the hydrated commits back to the original code change that caused the hydration. 176 177 Source Hydrator makes use of some custom git commit trailers to facilitate this tracing. A CI job that builds an image 178 and pushes an image bump to DRY manifests can use the following commit trailers to link the hydrated commit to the 179 code commit. 180 181 ```shell 182 git commit -m "Bump image to v1.2.3" \ 183 # Must be an RFC 5322 name 184 --trailer "Argocd-reference-commit-author: Author Name <author@example.com>" \ 185 # Must be a hex string 5-40 characters long 186 --trailer "Argocd-reference-commit-sha: <code-commit-sha>" \ 187 # The subject is the first line of the commit message. It cannot contain newlines. 188 --trailer "Argocd-reference-commit-subject: Commit message of the code commit" \ 189 # The body must be a valid JSON string, including opening and closing quotes 190 --trailer 'Argocd-reference-commit-body: "Commit message of the code commit\n\nSigned-off-by: Author Name <author@example.com>"' \ 191 # The repo URL must be a valid URL 192 --trailer "Argocd-reference-commit-repourl: https://git.example.com/owner/repo" \ 193 # The date must by in ISO 8601 format 194 --trailer "Argocd-reference-commit-date: 2025-06-09T13:50:18-04:00" 195 ``` 196 197 !!!note Newlines are not allowed 198 The commit trailers must not contain newlines. 199 200 So the full CI script might look something like this: 201 202 ```shell 203 # Clone code repo 204 git clone https://git.example.com/owner/repo.git 205 cd repo 206 207 # Build the image and get the new image tag 208 # <cusom build logic here> 209 210 # Get the commit information 211 author=$(git show -s --format="%an <%ae>") 212 sha=$(git rev-parse HEAD) 213 subject=$(git show -s --format='%s') 214 body=$(git show -s --format='%b') 215 jsonbody=$(jq -n --arg body "$body" '$body') 216 repourl=$(git remote get-url origin) 217 date=$(git show -s --format='%aI') 218 219 # Clone the dry source repo 220 git clone https://git.example.com/owner/deployment-repo.git 221 cd deployment-repo 222 223 # Bump the image in the dry manifests 224 # <custom bump logic here, e.g. `kustomize edit`> 225 226 # Commit the changes with the commit trailers 227 git commit -m "Bump image to v1.2.3" \ 228 --trailer "Argocd-reference-commit-author: $author" \ 229 --trailer "Argocd-reference-commit-sha: $sha" \ 230 --trailer "Argocd-reference-commit-subject: $subject" \ 231 --trailer "Argocd-reference-commit-body: $jsonbody" \ 232 --trailer "Argocd-reference-commit-repourl: $repourl" \ 233 --trailer "Argocd-reference-commit-date: $date" 234 ``` 235 236 The commit metadata will appear in the hydrated commit's root hydrator.metadata file: 237 238 ```json 239 { 240 "author": "CI <ci@example.com>", 241 "subject": "chore: bump image to b82add2", 242 "date": "2025-06-09T13:50:08-04:00", 243 "body": "Signed-off-by: CI <ci@example.com>\n", 244 "drySha": "6cb951525937865dced818bbdd78c89b2d2b3045", 245 "repoURL": "https://git.example.com/owner/manifests-repo", 246 "references": [ 247 { 248 "commit": { 249 "author": { 250 "name": "Author Name", 251 "email": "author@example.com" 252 }, 253 "sha": "b82add298aa045d3672880802d5305c5a8aaa46e", 254 "subject": "chore: make a change", 255 "body": "make a change\n\nSigned-off-by: Author Name <author@example.com>", 256 "repoURL": "https://git.example.com/owner/repo", 257 "date": "2025-06-09T13:50:18-04:00" 258 } 259 } 260 ] 261 } 262 ``` 263 264 The top-level "body" field contains the commit message of the DRY commit minus the subject line and any 265 `Argocd-reference-commit-*` trailers that were used in `references`. Unrecognized or invalid trailers are preserved in 266 the body. 267 268 Although `references` is an array, the source hydrator currently only supports a single related commit. If a trailer is 269 specified more than once, the last one will be used. 270 271 All trailers are optional. If a trailer is not specified, the corresponding field in the metadata will be omitted. 272 273 ## Commit Message Template 274 275 The commit message is generated using a [Go text/template](https://pkg.go.dev/text/template), optionally configured by the user via the argocd-cm ConfigMap. The template is rendered using the values from `hydrator.metadata`. The template can be multi-line, allowing users to define a subject line, body and optional trailers. To define the commit message template, you need to set the `sourceHydrator.commitMessageTemplate` field in argocd-cm ConfigMap. 276 277 The template may functions from the [Sprig function library](https://github.com/Masterminds/sprig). 278 279 ```yaml 280 apiVersion: v1 281 kind: ConfigMap 282 metadata: 283 name: argocd-cm 284 namespace: argocd 285 data: 286 sourceHydrator.commitMessageTemplate: | 287 {{.metadata.drySha | trunc 7}}: {{ .metadata.subject }} 288 {{- if .metadata.body }} 289 290 {{ .metadata.body }} 291 {{- end }} 292 {{ range $ref := .metadata.references }} 293 {{- if and $ref.commit $ref.commit.author }} 294 Co-authored-by: {{ $ref.commit.author }} 295 {{- end }} 296 {{- end }} 297 {{- if .metadata.author }} 298 Co-authored-by: {{ .metadata.author }} 299 {{- end }} 300 ``` 301 302 ### Credential Templates 303 304 Credential templates allow a single credential to be used for multiple repositories. The source hydrator supports credential templates. For example, if you setup credential templates for the URL prefix `https://github.com/argoproj`, these credentials will be used for all repositories with this URL as prefix (e.g. `https://github.com/argoproj/argocd-example-apps`) that do not have their own credentials configured. 305 For more information please refer [credential-template](private-repositories.md#credential-templates). 306 An example of repo-write-creds secret. 307 308 ```yaml 309 apiVersion: v1 310 kind: Secret 311 metadata: 312 name: private-repo 313 namespace: argocd 314 labels: 315 argocd.argoproj.io/secret-type: repo-write-creds 316 stringData: 317 type: git 318 url: https://github.com/argoproj 319 password: my-password 320 username: my-username 321 ``` 322 323 ## Limitations 324 325 ### Signature Verification 326 327 The source hydrator **does not currently support signature verification of the DRY sources it hydrates/commits**. It 328 also does not sign the commits it pushes to git, so if signature verification is enabled, the commits will fail 329 verification when Argo CD attempts to sync the hydrated manifests. 330 331 ### Project-Scoped Push Secrets 332 333 If all the Applications for a given destination repo/branch are under the same project, then the hydrator will use any 334 available project-scoped push secrets. If two Applications for a given repo/branch are in different projects, then the 335 hydrator will not be able to use a project-scoped push secret and will require a global push secret. 336 337 ### `manifest-generate-paths` Annotation Support 338 339 The source hydrator does not currently support the [manifest-generate-paths annotation](../operator-manual/high_availability.md#manifest-paths-annotation) 340 for work avoidance on hydration of dry commits. In other words, the source hydrator is not able to skip hydration of dry 341 commits that have not changed relevant files. 342 343 The application controller _does_ honor the `manifest-generate-paths` annotation when syncing the hydrated manifests. 344 So if your application hydrates to the `foo` directory, and the `manifest-generate-paths` annotation is set to `foo`, 345 then the application controller will not re-hydrate the manifests after a commit that only affects files in the `bar` 346 directory. 347 348 ## Prerequisites 349 350 ### Handle Secrets on the Destination Cluster 351 352 Do not use the source hydrator with any tool that injects secrets into your manifests as part of the hydration process 353 (for example, Helm with SOPS or the Argo CD Vault Plugin). These secrets would be committed to git. Instead, use a 354 secrets operator that populates the secret values on the destination cluster. 355 356 ## Best Practices 357 358 ### Make Hydration Deterministic 359 360 The source hydrator should be deterministic. For a given dry source commit, the hydrator should always produce the same 361 hydrated manifests. This means that the hydrator should not rely on external state or configuration that is not stored 362 in git. 363 364 Examples of non-deterministic hydration: 365 366 * A Helm chart using unpinned dependencies 367 * A Helm chart is using a non-deterministic template function such as `randAlphaNum` or `lookup` 368 * [Config Management Plugins](../operator-manual/config-management-plugins.md) which retrieve non-git state, such as secrets 369 * Kustomize manifests referencing unpinned remote bases 370 371 ### Enable Branch Protection 372 373 Argo CD should be the only thing pushing hydrated manifests to the hydrated branches. To prevent other tools or users 374 from pushing to the hydrated branches, enable branch protection in your SCM. 375 376 It is best practice to prefix the hydrated branches with a common prefix, such as `environments/`. This makes it easier 377 to configure branch protection rules on the destination repository. 378 379 !!! note 380 To maintain reproducibility and determinism in the Hydrator’s output, 381 Argo CD-specific metadata (such as `argocd.argoproj.io/tracking-id`) is 382 not written to Git during hydration. These annotations are added dynamically 383 during application sync and comparison.