<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Pull Model – TechBlog about OpenShift/Ansible/Satellite and much more</title><link>https://blog.stderr.at/tags/pull-model/</link><description>TechBlog about OpenShift/Ansible/Satellite and much more</description><generator>Hugo 0.164.0</generator><language>en-us</language><copyright>Toni Schmidbauer &amp; Thomas Jungbauer</copyright><lastBuildDate>Fri, 23 Oct 2026 00:00:00 +0200</lastBuildDate><atom:link href="https://blog.stderr.at/tags/pull-model/index.xml" rel="self" type="application/rss+xml"/><item><title>[Ep.19] ACM and OpenShift GitOps: push, pull and the Argo CD Agent</title><link>https://blog.stderr.at/gitopscollection/2026-10-23-acm-gitops-push-pull-agent/</link><guid isPermaLink="true">https://blog.stderr.at/gitopscollection/2026-10-23-acm-gitops-push-pull-agent/</guid><pubDate>Fri, 23 Oct 2026 00:00:00 +0200</pubDate><dc:creator>Articles by Thomas Jungbauer</dc:creator><category>OpenShift</category><category>GitOps</category><description>How ACM registers clusters in OpenShift GitOps: push model, pull model and the Argo CD Agent add-on, with a step-by-step setup for each approach.</description><content:encoded><![CDATA[<div class="paragraph">
<p>In <a href="/gitopscollection/2026-10-20-openshift-gitops-vs-acm/">[Ep.18]</a> I compared OpenShift GitOps and Advanced Cluster Management and ended with the statement that they are layers, not competitors. ACM knows the fleet and Argo CD delivers the content. This part is about the glue between the two: how ACM registers its clusters in Argo CD, and the three ways an Argo CD Application can reach a managed cluster: the <strong>push model</strong>, the <strong>pull model</strong> and the <strong>Argo CD Agent</strong>.</p>
</div>
<div class="paragraph">
<p>For each of them I will explain what it actually is, how the data flows, and how to set it up step by step.</p>
</div>
<div class="paragraph">
<p>The topic turned out to be too big for a single article, so I have split it into multiple parts and maybe I should have split into even more. This is the second part:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><a href="/gitopscollection/2026-10-20-openshift-gitops-vs-acm/">[Ep.18] OpenShift GitOps or Advanced Cluster Management? Choosing the right tool</a> What each tool does, where they overlap and a decision guide.</p>
</li>
<li>
<p><strong>[Ep.19] ACM and OpenShift GitOps: push, pull and the Argo CD Agent</strong> (this article): how ACM registers clusters in Argo CD, and how to set up each of the three delivery models.</p>
</li>
<li>
<p><a href="/gitopscollection/2026-10-24-acm-policies-with-gitops/">[Ep.20] Managing ACM Policies with OpenShift GitOps</a>: policies as code, the PolicyGenerator, hub templates and bootstrapping Argo CD with a policy.</p>
</li>
</ol>
</div>
<div class="admonitionblock note">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Everything in this series was written against <strong>OpenShift GitOps 1.22</strong> (Argo CD 3.5) and <strong>Advanced Cluster Management 2.17</strong> (Open Cluster Management). Technology Preview and GA states move quickly in both products, so check the release notes of your versions before you commit to a design.
</td>
</tr>
</tbody></table>
</div>
<div class="admonitionblock note">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
While I am using the term &#34;Advanced Cluster Management&#34; in this article, I am referring to the product formerly known as Open Cluster Management. Same counts for Argo CD and OpenShift GitOps.
</td>
</tr>
</tbody></table>
</div>
<div class="sect1">
<h2 id="_the_setup">The setup</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Before we dig into the models, let’s talk about my lab setup.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>There are 3 clusters:</p>
<div class="ulist">
<ul>
<li>
<p><code>hub</code> is the ACM hub with openshift-gitops installed.</p>
</li>
<li>
<p><code>bm</code> is a bare metal cluster with a single node.</p>
</li>
<li>
<p><code>sno</code> a single node cluster.</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="paragraph">
<p>The <code>hub</code> cluster is the starting point. Argo CD on the hub currently knows one cluster: <code>in-cluster</code>. This entity is automatically added during installation of Argo CD and it is the cluster Argo CD is hosted on.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/gitopscollection/images/ep-19/argocd-settings-clusters.png" alt="Argo CD settings clusters"/>
</div>
<div class="title">Figure 1. Argo CD settings clusters</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_prerequisites_for_all_models">Prerequisites for all models</h2>
<div class="sectionbody">
<div class="olist arabic">
<ol class="arabic">
<li>
<p>An ACM hub with the OpenShift GitOps Operator installed (the default instance in <code>openshift-gitops</code>).</p>
</li>
<li>
<p>The <strong>ManagedServiceAccount add-on</strong> enabled. ACM uses it to create and rotate the tokens for the push and the pull model. It is enabled by default with current versions of the multicluster engine, but check it if you upgraded from an old release: <code>oc get managedclusteraddon managed-serviceaccount -n &lt;cluster-name&gt;</code>.</p>
<div class="listingblock linenums">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">oc get managedclusteraddon -A | grep managed-serviceaccount                                                                                                                               ─╯
bm              managed-serviceaccount        True                   False
local-cluster   managed-serviceaccount        True                   False
sno             managed-serviceaccount        True                   False</code></pre>
</div>
</div>
</li>
<li>
<p>The managed clusters <code>sno</code> and <code>bm</code> are in a ManagedClusterSet. In this article I called it <code>production</code>.</p>
<div class="openblock">
<div class="content">
<div class="listingblock linenums">
<div class="title">ManagedClusterSet production</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">apiVersion: cluster.open-cluster-management.io/v1beta2
kind: ManagedClusterSet
metadata:
  name: production <i class="conum" data-value="1"></i><b>(1)</b>
spec:
  clusterSelector:
    selectorType: ExclusiveClusterSetLabel <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Cluster-scoped, so there is no namespace.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The default. A cluster joins the set through the label <code>cluster.open-cluster-management.io/clusterset=production</code>. A cluster can only be in one such set at a time.</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>Add the clusters to the set by labelling them.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">oc label managedcluster bm cluster.open-cluster-management.io/clusterset=production --overwrite
oc label managedcluster sno cluster.open-cluster-management.io/clusterset=production --overwrite</code></pre>
</div>
</div>
<div class="paragraph">
<p>Check the result:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">oc get managedclusterset production
oc get managedclusters -l cluster.open-cluster-management.io/clusterset=production</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">NAME         EMPTY   AGE
production   False   53s
NAME   HUB ACCEPTED   MANAGED CLUSTER URLS                   JOINED   AVAILABLE   AGE
bm     true           https://api.bm.stdin.at:6443           True     True        172m
sno    true           https://api.sno.aws.ispworld.at:6443   True     True        51m</code></pre>
</div>
</div>
</div>
</div>
</li>
<li>
<p>A <code>ManagedClusterSetBinding</code> for the cluster set <code>production</code> in the <strong>openshift-gitops</strong> namespace.</p>
<div class="openblock">
<div class="content">
<div class="admonitionblock warning">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
It binds a ManagedClusterSet resource to a <strong>namespace</strong>. This is easy to forget: the Placement for Argo CD lives in the <strong>Argo CD namespace</strong>, so the cluster set must be bound there. Without this binding, the Placement does not find a single cluster.
</td>
</tr>
</tbody></table>
</div>
<div class="listingblock linenums">
<div class="title">ManagedClusterSetBinding in the Argo CD namespace</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">apiVersion: cluster.open-cluster-management.io/v1beta2
kind: ManagedClusterSetBinding
metadata:
  name: production
  namespace: openshift-gitops
spec:
  clusterSet: production</code></pre>
</div>
</div>
<div class="paragraph">
<p>Verify that your managed clusters are in the cluster set:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">oc get managedclusters -l cluster.open-cluster-management.io/clusterset=production</code></pre>
</div>
</div>
</div>
</div>
</li>
</ol>
</div>
<div class="paragraph">
<p>The following image shows the currently created objects in the hub cluster:</p>
</div>
<div class="paragraph">
<div class="imageblock animated-svg" data-animated-svg>
  <div class="content">
<svg
   width="1200"
   height="600"
   viewBox="0 0 1200 600"
   role="img"
   aria-label="Objects created in the prerequisites: GitOps instance, ManagedServiceAccount add-on, ManagedClusterSet production and its binding to openshift-gitops"
   version="1.1"
   id="ase622760-svg59"
  
  
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:dc="http://purl.org/dc/elements/1.1/">
  
  <title
     id="ase622760-title1">Objects created in the prerequisites: GitOps instance, ManagedServiceAccount add-on, ManagedClusterSet production and its binding to openshift-gitops</title>
  <defs
     id="ase622760-defs2">
    <rect
       x="0"
       y="529.88306"
       width="460.97997"
       height="100.61071"
       id="ase622760-rect63" />
    <marker
       id="ase622760-arrow-0F766E"
       viewBox="0 0 10 10"
       refX="8.5"
       refY="5"
       markerWidth="7"
       markerHeight="7"
       orient="auto-start-reverse">
      <path
         d="M 0 0 L 10 5 L 0 10 z"
         fill="#0F766E"
         id="ase622760-path1" />
    </marker>
    <marker
       id="ase622760-arrow-4B5563"
       viewBox="0 0 10 10"
       refX="8.5"
       refY="5"
       markerWidth="7"
       markerHeight="7"
       orient="auto-start-reverse">
      <path
         d="M 0 0 L 10 5 L 0 10 z"
         fill="#4B5563"
         id="ase622760-path2" />
    </marker>
  </defs>
  <rect
     width="1200"
     height="600"
     fill="#FFFFFF"
     id="ase622760-rect2" />
  <rect
     x="20"
     y="20"
     width="420"
     height="500"
     rx="14"
     fill="#F8FAFC"
     stroke="#E2E8F0"
     stroke-width="1.5"
     id="ase622760-rect3" />
  <text
     x="40"
     y="48"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="700"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text3">HUB  ·  CLUSTER-SCOPED</text>
  <rect
     x="40"
     y="64"
     width="170"
     height="84"
     rx="10"
     fill="#EEF2FF"
     stroke="#4338CA"
     stroke-width="1.6"
     id="ase622760-rect4" />
  <text
     x="56"
     y="93"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15"
     font-weight="700"
     fill="#4338CA"
     text-anchor="start"
     id="ase622760-text4">bm</text>
  <text
     x="56"
     y="116"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5px"
     font-weight="400"
     fill="#1f2937"
     text-anchor="start"
     id="ase622760-text5"><tspan
       style="font-weight:bold"
       id="ase622760-tspan60">ManagedCluster</tspan></text>
  <rect
     x="40"
     y="168"
     width="170"
     height="84"
     rx="10"
     fill="#EEF2FF"
     stroke="#4338CA"
     stroke-width="1.6"
     id="ase622760-rect5" />
  <text
     x="56"
     y="197"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15"
     font-weight="700"
     fill="#4338CA"
     text-anchor="start"
     id="ase622760-text6">sno</text>
  <text
     x="56"
     y="220"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5px"
     font-weight="400"
     fill="#1f2937"
     text-anchor="start"
     id="ase622760-text7"><tspan
       style="font-weight:bold"
       id="ase622760-tspan61">ManagedCluster</tspan></text>
  <rect
     x="40"
     y="272"
     width="170"
     height="84"
     rx="10"
     fill="#FFFFFF"
     stroke="#4338CA"
     stroke-width="1.6"
     stroke-dasharray="6 4"
     id="ase622760-rect7" />
  <text
     x="56"
     y="301"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15"
     font-weight="700"
     fill="#4338CA"
     text-anchor="start"
     id="ase622760-text8">local-cluster</text>
  <text
     x="56"
     y="324"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5px"
     font-weight="400"
     fill="#4b5563"
     text-anchor="start"
     id="ase622760-text9"><tspan
   style="font-weight:bold"
   id="ase622760-tspan62">ManagedCluster</tspan> (the</text>
  <text
     x="56"
     y="340"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5px"
     font-weight="400"
     fill="#4b5563"
     text-anchor="start"
     id="ase622760-text10">hub), not in production Set</text>
  <rect
     x="250"
     y="100"
     width="170"
     height="120"
     rx="10"
     fill="#E6F4F1"
     stroke="#0F766E"
     stroke-width="1.6"
     id="ase622760-rect10" />
  <text
     x="266"
     y="129"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15px"
     font-weight="700"
     fill="#0f766e"
     text-anchor="start"
     id="ase622760-text12">production</text>
  <text
     x="266"
     y="152"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5px"
     font-weight="400"
     fill="#1f2937"
     text-anchor="start"
     id="ase622760-text13"><tspan
       style="font-weight:bold"
       id="ase622760-tspan59">ManagedClusterSet,</tspan></text>
  <text
     x="266"
     y="168"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#1F2937"
     text-anchor="start"
     id="ase622760-text14">one exclusive set per</text>
  <text
     x="266"
     y="184"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#1F2937"
     text-anchor="start"
     id="ase622760-text15">cluster</text>
  <path
     d="M 212 106 L 248 140"
     fill="none"
     stroke="#0F766E"
     stroke-width="2"
     stroke-linejoin="round"
     marker-end="url(#ase622760-arrow-0F766E)"
     id="ase622760-path15" />
  <path
     d="M 212 210 L 248 190"
     fill="none"
     stroke="#0F766E"
     stroke-width="2"
     stroke-linejoin="round"
     marker-end="url(#ase622760-arrow-0F766E)"
     id="ase622760-path16" />
  <text
     x="40"
     y="392"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="500"
     fill="#1F2937"
     text-anchor="start"
     id="ase622760-text16">bm and sno join the set with the label</text>
  <text
     x="40"
     y="412"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="600"
     fill="#0F766E"
     text-anchor="start"
     id="ase622760-text17">cluster.open-cluster-management.io/</text>
  <text
     x="40"
     y="430"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="600"
     fill="#0F766E"
     text-anchor="start"
     id="ase622760-text18">clusterset=production</text>
  <text
     x="40"
     y="462"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text19">The label is the only link between a</text>
  <text
     x="40"
     y="480"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text20">cluster and its set.</text>
  <rect
     x="460"
     y="20"
     width="720"
     height="500"
     rx="14"
     fill="#F8FAFC"
     stroke="#E2E8F0"
     stroke-width="1.5"
     id="ase622760-rect20" />
  <text
     x="480"
     y="48"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="700"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text21">HUB  ·  NAMESPACES</text>
  <rect
     x="480"
     y="52"
     width="680"
     height="196"
     rx="10"
     fill="#FFFFFF"
     stroke="#CBD5E1"
     stroke-width="1.2"
     id="ase622760-rect21" />
  <text
     x="496"
     y="76"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="700"
     fill="#C2410C"
     text-anchor="start"
     id="ase622760-text22">openshift-gitops</text>
  <rect
     x="496"
     y="92"
     width="200"
     height="132"
     rx="10"
     fill="#FDF0E8"
     stroke="#C2410C"
     stroke-width="1.6"
     id="ase622760-rect22" />
  <text
     x="512"
     y="121"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15px"
     font-weight="700"
     fill="#c2410c"
     text-anchor="start"
     id="ase622760-text24">ArgoCD</text>
  <text
     x="512"
     y="144"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#1F2937"
     text-anchor="start"
     id="ase622760-text25">openshift-gitops, the default</text>
  <text
     x="512"
     y="160"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#1F2937"
     text-anchor="start"
     id="ase622760-text26">instance of the OpenShift</text>
  <text
     x="512"
     y="176"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5px"
     font-weight="400"
     fill="#1f2937"
     text-anchor="start"
     id="ase622760-text27">GitOps Operator</text>
  <rect
     x="716"
     y="92"
     width="210"
     height="132"
     rx="10"
     fill="#E6F4F1"
     stroke="#0F766E"
     stroke-width="1.6"
     id="ase622760-rect27" />
  <text
     x="732"
     y="121"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15px"
     font-weight="700"
     fill="#0f766e"
     text-anchor="start"
     id="ase622760-text29">Set binding</text>
  <text
     x="732"
     y="144"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5px"
     font-weight="400"
     fill="#1f2937"
     text-anchor="start"
     id="ase622760-text30"><tspan
       style="font-weight:bold"
       id="ase622760-tspan63">ManagedClusterSetBinding</tspan></text>
  <text
     x="732"
     y="160"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#1F2937"
     text-anchor="start"
     id="ase622760-text31">production: makes the set</text>
  <text
     x="732"
     y="176"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#1F2937"
     text-anchor="start"
     id="ase622760-text32">usable in this namespace</text>
  <rect
     x="946"
     y="92"
     width="196"
     height="132"
     rx="10"
     fill="#FFFFFF"
     stroke="#4B5563"
     stroke-width="1.4"
     stroke-dasharray="6 4"
     id="ase622760-rect32" />
  <text
     x="962"
     y="121"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15"
     font-weight="700"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text33">Placement</text>
  <text
     x="962"
     y="144"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text34">next step: selects clusters</text>
  <text
     x="962"
     y="160"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text35">from the bound set</text>
  <path
     d="M 928 158 L 944 158"
     fill="none"
     stroke="#4B5563"
     stroke-width="2"
     stroke-linejoin="round"
     stroke-dasharray="4 3"
     marker-end="url(#ase622760-arrow-4B5563)"
     id="ase622760-path35" />
  <path
     d="M 821 226 L 821 262 L 330 262 L 330 222"
     fill="none"
     stroke="#0F766E"
     stroke-width="2"
     stroke-linejoin="round"
     marker-end="url(#ase622760-arrow-0F766E)"
     id="ase622760-path36" />
  <circle class="flow-dot" style="--d:0.30s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 821 226 L 821 262 L 330 262 L 330 222" begin="indefinite" data-begin="0.30" dur="2.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <rect
     x="500"
     y="252"
     width="168"
     height="20"
     fill="#F8FAFC"
     id="ase622760-rect36" />
  <text
     x="584"
     y="266"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="600"
     fill="#0F766E"
     text-anchor="middle"
     id="ase622760-text36">clusterSet: production</text>
  <text
     x="480"
     y="306"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="700"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text37">Cluster namespaces (one per managed cluster, created by ACM)</text>
  <rect
     x="480"
     y="318"
     width="216"
     height="182"
     rx="10"
     fill="#FFFFFF"
     stroke="#CBD5E1"
     stroke-width="1.2"
     id="ase622760-rect37" />
  <text
     x="496"
     y="342"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="700"
     fill="#4338CA"
     text-anchor="start"
     id="ase622760-text38">bm</text>
  <rect
     x="492"
     y="356"
     width="192"
     height="128"
     rx="10"
     fill="#FFFFFF"
     stroke="#0F766E"
     stroke-width="1.6"
     stroke-dasharray="6 4"
     id="ase622760-rect38" />
  <text
     x="508"
     y="385"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15px"
     font-weight="700"
     fill="#0f766e"
     text-anchor="start"
     id="ase622760-text40">Add-on</text>
  <text
     x="508"
     y="408"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text41">ManagedClusterAddOn</text>
  <text
     x="508"
     y="424"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text42">managed-serviceaccount,</text>
  <text
     x="508"
     y="440"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text43">enabled by default</text>
  <rect
     x="712"
     y="318"
     width="216"
     height="182"
     rx="10"
     fill="#FFFFFF"
     stroke="#CBD5E1"
     stroke-width="1.2"
     id="ase622760-rect43" />
  <text
     x="728"
     y="342"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="700"
     fill="#4338CA"
     text-anchor="start"
     id="ase622760-text44">sno</text>
  <rect
     x="724"
     y="356"
     width="192"
     height="128"
     rx="10"
     fill="#FFFFFF"
     stroke="#0F766E"
     stroke-width="1.6"
     stroke-dasharray="6 4"
     id="ase622760-rect44" />
  <text
     x="740"
     y="385"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15px"
     font-weight="700"
     fill="#0f766e"
     text-anchor="start"
     id="ase622760-text46">Add-on</text>
  <text
     x="740"
     y="408"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text47">ManagedClusterAddOn</text>
  <text
     x="740"
     y="424"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text48">managed-serviceaccount,</text>
  <text
     x="740"
     y="440"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text49">enabled by default</text>
  <rect
     x="944"
     y="318"
     width="216"
     height="182"
     rx="10"
     fill="#FFFFFF"
     stroke="#CBD5E1"
     stroke-width="1.2"
     id="ase622760-rect49" />
  <text
     x="960"
     y="342"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="700"
     fill="#4338CA"
     text-anchor="start"
     id="ase622760-text50">local-cluster</text>
  <rect
     x="956"
     y="356"
     width="192"
     height="128"
     rx="10"
     fill="#FFFFFF"
     stroke="#0F766E"
     stroke-width="1.6"
     stroke-dasharray="6 4"
     id="ase622760-rect50" />
  <text
     x="972"
     y="385"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15px"
     font-weight="700"
     fill="#0f766e"
     text-anchor="start"
     id="ase622760-text52">Add-on</text>
  <text
     x="972"
     y="408"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text53">ManagedClusterAddOn</text>
  <text
     x="972"
     y="424"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text54">managed-serviceaccount,</text>
  <text
     x="972"
     y="440"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="ase622760-text55">enabled by default</text>
  <rect
     x="40"
     y="557"
     width="22"
     height="14"
     rx="3"
     fill="#E6F4F1"
     stroke="#0F766E"
     stroke-width="1.4"
     id="ase622760-rect55" />
  <text
     x="70"
     y="569"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12px"
     font-weight="400"
     fill="#1f2937"
     text-anchor="start"
     id="ase622760-text56">created by you</text>
  <rect
     x="166.234375"
     y="557"
     width="22"
     height="14"
     rx="3"
     fill="#FFFFFF"
     stroke="#0F766E"
     stroke-width="1.4"
     stroke-dasharray="4 3"
     id="ase622760-rect56" />
  <text
     x="196.23438"
     y="569"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12px"
     font-weight="400"
     fill="#1f2937"
     text-anchor="start"
     id="ase622760-text57">generated by ACM</text>
  
<circle class="flow-dot" style="--d:0.00s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 212 106 L 248 140" begin="indefinite" data-begin="0.00" dur="1.20s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:0.60s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 212 210 L 248 190" begin="indefinite" data-begin="0.60" dur="1.20s" repeatCount="indefinite" calcMode="linear"/></circle>
  </svg>
</div>
  <div class="title">Currently created objects in the hub cluster</div>
</div>

</div>
<hr/>
</div>
</div>
<div class="sect1">
<h2 id="_the_bridge_placement_gitopscluster_and_applicationset">The bridge: Placement, GitOpsCluster and ApplicationSet</h2>
<div class="sectionbody">
<div class="paragraph">
<p>So far, we only created the prerequisites for all three models. When we now look at the models all three of them start with the same chain of objects on the hub:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>a <strong>Placement</strong> decides <em>which</em> managed clusters are used,</p>
</li>
<li>
<p>a <strong>GitOpsCluster</strong> makes these clusters known to an Argo CD instance, and</p>
</li>
<li>
<p>an <strong>ApplicationSet</strong> creates one Application per selected cluster.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>The GitOpsCluster is the actual bridge between ACM and Argo CD. ACM knows the clusters, Argo CD knows the Applications, and the GitOpsCluster translates one into the other.</p>
</div>
<div class="paragraph">
<p>What differs between the models is what happens <em>after</em> this chain: <em>who</em> talks to the managed cluster, <em>in which direction</em> the network connection goes, and <em>where</em> the reconciliation happens. That is why the GitOpsCluster and the ApplicationSet look different in each model and are created in the model sections below.</p>
</div>
<div class="paragraph">
<p>The Placement is identical for all three, so we create it now.</p>
</div>
<div class="sect2">
<h3 id="_the_placement">The Placement</h3>
<div class="paragraph">
<p>The Placement lives in the Argo CD namespace, next to the ManagedClusterSetBinding from the prerequisites. It selects the OpenShift clusters of the cluster set <code>production</code>.</p>
</div>
<div class="listingblock linenums">
<div class="title">Placement in the Argo CD namespace</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">apiVersion: cluster.open-cluster-management.io/v1beta1
kind: Placement
metadata:
  name: production-clusters
  namespace: openshift-gitops <i class="conum" data-value="1"></i><b>(1)</b>
spec:
  clusterSets:
    - production <i class="conum" data-value="2"></i><b>(2)</b>
  predicates:
    - requiredClusterSelector: <i class="conum" data-value="3"></i><b>(3)</b>
        labelSelector:
          matchExpressions:
            - key: vendor
              operator: In
              values:
                - OpenShift
  tolerations: <i class="conum" data-value="4"></i><b>(4)</b>
    - key: cluster.open-cluster-management.io/unreachable
      operator: Exists
    - key: cluster.open-cluster-management.io/unavailable
      operator: Exists</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The same namespace as the GitOpsCluster that will reference it.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Represents the ManagedClusterSets from which the ManagedClusters are selected.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>The predicate to select only OpenShift clusters.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>By default a Placement drops clusters that are unreachable. For GitOps we want to keep them, so that a short network hiccup does not remove the cluster secret and, worst case, the generated Applications.</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>The result of a Placement is a <strong>PlacementDecision</strong>. Check which clusters were selected:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">oc get placementdecisions -n openshift-gitops \
  -l cluster.open-cluster-management.io/placement=production-clusters \
  -o jsonpath=&#39;{.items[*].status.decisions[*].clusterName}{&#34;\n&#34;}&#39;</code></pre>
</div>
</div>
<div class="paragraph">
<p>With <code>bm</code> and <code>sno</code> in the set, the output is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-none hljs">bm sno</code></pre>
</div>
</div>
<div class="paragraph">
<p>The Placement and its PlacementDecision complete the picture on the hub:</p>
</div>
<div class="paragraph">
<div class="imageblock animated-svg" data-animated-svg>
  <div class="content">
<svg
   width="1200"
   height="720"
   viewBox="0 0 1200 720"
   role="img"
   aria-label="Objects on the hub: managed clusters, ManagedClusterSet production, the binding to openshift-gitops, the Placement production-clusters with its PlacementDecision (bm, sno), and the ManagedServiceAccount add-ons"
   version="1.1"
   id="as5de8cd1-svg59"
  
  
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:dc="http://purl.org/dc/elements/1.1/">
  
  <title
     id="as5de8cd1-title1">Objects on the hub: managed clusters, ManagedClusterSet production, the binding to openshift-gitops, the Placement production-clusters with its PlacementDecision (bm, sno), and the ManagedServiceAccount add-ons</title>
  <defs
     id="as5de8cd1-defs2">
    <rect
       x="0"
       y="529.88306"
       width="460.97997"
       height="100.61071"
       id="as5de8cd1-rect63" />
    <marker
       id="as5de8cd1-arrow-0F766E"
       viewBox="0 0 10 10"
       refX="8.5"
       refY="5"
       markerWidth="7"
       markerHeight="7"
       orient="auto-start-reverse">
      <path
         d="M 0 0 L 10 5 L 0 10 z"
         fill="#0F766E"
         id="as5de8cd1-path1" />
    </marker>
    <marker
       id="as5de8cd1-arrow-4B5563"
       viewBox="0 0 10 10"
       refX="8.5"
       refY="5"
       markerWidth="7"
       markerHeight="7"
       orient="auto-start-reverse">
      <path
         d="M 0 0 L 10 5 L 0 10 z"
         fill="#4B5563"
         id="as5de8cd1-path2" />
    </marker>
  </defs>
  <rect
     width="1200"
     height="720"
     fill="#FFFFFF"
     id="as5de8cd1-rect2" />
  <rect
     x="20"
     y="20"
     width="420"
     height="614"
     rx="14"
     fill="#F8FAFC"
     stroke="#E2E8F0"
     stroke-width="1.5"
     id="as5de8cd1-rect3" />
  <text
     x="40"
     y="48"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="700"
     fill="#4B5563"
     text-anchor="start"
     id="as5de8cd1-text3">HUB  ·  CLUSTER-SCOPED</text>
  <rect
     x="40"
     y="64"
     width="170"
     height="84"
     rx="10"
     fill="#EEF2FF"
     stroke="#4338CA"
     stroke-width="1.6"
     id="as5de8cd1-rect4" />
  <text
     x="56"
     y="93"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15"
     font-weight="700"
     fill="#4338CA"
     text-anchor="start"
     id="as5de8cd1-text4">bm</text>
  <text
     x="56"
     y="116"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5px"
     font-weight="400"
     fill="#1f2937"
     text-anchor="start"
     id="as5de8cd1-text5"><tspan
       style="font-weight:bold"
       id="as5de8cd1-tspan60">ManagedCluster</tspan></text>
  <rect
     x="40"
     y="168"
     width="170"
     height="84"
     rx="10"
     fill="#EEF2FF"
     stroke="#4338CA"
     stroke-width="1.6"
     id="as5de8cd1-rect5" />
  <text
     x="56"
     y="197"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15"
     font-weight="700"
     fill="#4338CA"
     text-anchor="start"
     id="as5de8cd1-text6">sno</text>
  <text
     x="56"
     y="220"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5px"
     font-weight="400"
     fill="#1f2937"
     text-anchor="start"
     id="as5de8cd1-text7"><tspan
       style="font-weight:bold"
       id="as5de8cd1-tspan61">ManagedCluster</tspan></text>
  <rect
     x="40"
     y="272"
     width="170"
     height="84"
     rx="10"
     fill="#FFFFFF"
     stroke="#4338CA"
     stroke-width="1.6"
     stroke-dasharray="6 4"
     id="as5de8cd1-rect7" />
  <text
     x="56"
     y="301"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15"
     font-weight="700"
     fill="#4338CA"
     text-anchor="start"
     id="as5de8cd1-text8">local-cluster</text>
  <text
     x="56"
     y="324"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5px"
     font-weight="400"
     fill="#4b5563"
     text-anchor="start"
     id="as5de8cd1-text9"><tspan
   style="font-weight:bold"
   id="as5de8cd1-tspan62">ManagedCluster</tspan> (the</text>
  <text
     x="56"
     y="340"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5px"
     font-weight="400"
     fill="#4b5563"
     text-anchor="start"
     id="as5de8cd1-text10">hub), not in production Set</text>
  <rect
     x="250"
     y="100"
     width="170"
     height="120"
     rx="10"
     fill="#E6F4F1"
     stroke="#0F766E"
     stroke-width="1.6"
     id="as5de8cd1-rect10" />
  <text
     x="266"
     y="129"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15px"
     font-weight="700"
     fill="#0f766e"
     text-anchor="start"
     id="as5de8cd1-text12">production</text>
  <text
     x="266"
     y="152"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5px"
     font-weight="400"
     fill="#1f2937"
     text-anchor="start"
     id="as5de8cd1-text13"><tspan
       style="font-weight:bold"
       id="as5de8cd1-tspan59">ManagedClusterSet,</tspan></text>
  <text
     x="266"
     y="168"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#1F2937"
     text-anchor="start"
     id="as5de8cd1-text14">one exclusive set per</text>
  <text
     x="266"
     y="184"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#1F2937"
     text-anchor="start"
     id="as5de8cd1-text15">cluster</text>
  <path
     d="M 212 106 L 248 140"
     fill="none"
     stroke="#0F766E"
     stroke-width="2"
     stroke-linejoin="round"
     marker-end="url(#as5de8cd1-arrow-0F766E)"
     id="as5de8cd1-path15" />
  <path
     d="M 212 210 L 248 190"
     fill="none"
     stroke="#0F766E"
     stroke-width="2"
     stroke-linejoin="round"
     marker-end="url(#as5de8cd1-arrow-0F766E)"
     id="as5de8cd1-path16" />
  <text
     x="40"
     y="392"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="500"
     fill="#1F2937"
     text-anchor="start"
     id="as5de8cd1-text16">bm and sno join the set with the label</text>
  <text
     x="40"
     y="412"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="600"
     fill="#0F766E"
     text-anchor="start"
     id="as5de8cd1-text17">cluster.open-cluster-management.io/</text>
  <text
     x="40"
     y="430"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="600"
     fill="#0F766E"
     text-anchor="start"
     id="as5de8cd1-text18">clusterset=production</text>
  <text
     x="40"
     y="462"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="as5de8cd1-text19">The label is the only link between a</text>
  <text
     x="40"
     y="480"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="as5de8cd1-text20">cluster and its set.</text>
  <rect
     x="460"
     y="20"
     width="720"
     height="614"
     rx="14"
     fill="#F8FAFC"
     stroke="#E2E8F0"
     stroke-width="1.5"
     id="as5de8cd1-rect20" />
  <text
     x="480"
     y="48"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="700"
     fill="#4B5563"
     text-anchor="start"
     id="as5de8cd1-text21">HUB  ·  NAMESPACES</text>
  <rect
     x="480"
     y="52"
     width="680"
     height="344"
     rx="10"
     fill="#FFFFFF"
     stroke="#CBD5E1"
     stroke-width="1.2"
     id="as5de8cd1-rect21" />
  <text
     x="496"
     y="76"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="700"
     fill="#C2410C"
     text-anchor="start"
     id="as5de8cd1-text22">openshift-gitops</text>
  <rect
     x="496"
     y="92"
     width="200"
     height="132"
     rx="10"
     fill="#FDF0E8"
     stroke="#C2410C"
     stroke-width="1.6"
     id="as5de8cd1-rect22" />
  <text
     x="512"
     y="121"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15px"
     font-weight="700"
     fill="#c2410c"
     text-anchor="start"
     id="as5de8cd1-text24">ArgoCD</text>
  <text
     x="512"
     y="144"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#1F2937"
     text-anchor="start"
     id="as5de8cd1-text25">openshift-gitops, the default</text>
  <text
     x="512"
     y="160"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#1F2937"
     text-anchor="start"
     id="as5de8cd1-text26">instance of the OpenShift</text>
  <text
     x="512"
     y="176"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5px"
     font-weight="400"
     fill="#1f2937"
     text-anchor="start"
     id="as5de8cd1-text27">GitOps Operator</text>
  <rect
     x="716"
     y="92"
     width="210"
     height="132"
     rx="10"
     fill="#E6F4F1"
     stroke="#0F766E"
     stroke-width="1.6"
     id="as5de8cd1-rect27" />
  <text
     x="732"
     y="121"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15px"
     font-weight="700"
     fill="#0f766e"
     text-anchor="start"
     id="as5de8cd1-text29">Set binding</text>
  <text
     x="732"
     y="144"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5px"
     font-weight="400"
     fill="#1f2937"
     text-anchor="start"
     id="as5de8cd1-text30"><tspan
       style="font-weight:bold"
       id="as5de8cd1-tspan63">ManagedClusterSetBinding</tspan></text>
  <text
     x="732"
     y="160"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#1F2937"
     text-anchor="start"
     id="as5de8cd1-text31">production: makes the set</text>
  <text
     x="732"
     y="176"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#1F2937"
     text-anchor="start"
     id="as5de8cd1-text32">usable in this namespace</text>
  <rect
     x="946"
     y="92"
     width="196"
     height="132"
     rx="10"
     fill="#E6F4F1"
     stroke="#0F766E"
     stroke-width="1.6"
    
     id="as5de8cd1-rect32" />
  <text
     x="962"
     y="121"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15"
     font-weight="700"
     fill="#0F766E"
     text-anchor="start"
     id="as5de8cd1-text33">Placement</text>
  <text
     x="962"
     y="144"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#1F2937"
     text-anchor="start"
     id="as5de8cd1-text34"><tspan style="font-weight:bold" id="as5de8cd1-tspan-placement-name">production-clusters</tspan></text>
  <text
     x="962"
     y="160"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#1F2937"
     text-anchor="start"
     id="as5de8cd1-text35">clusterSets: production</text>
  <text x="962" y="176" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start" id="as5de8cd1-text-placement-pred">vendor In OpenShift</text>
  <path
     d="M 928 158 L 944 158"
     fill="none"
     stroke="#0F766E"
     stroke-width="2"
     stroke-linejoin="round"
    
     marker-end="url(#as5de8cd1-arrow-0F766E)"
     id="as5de8cd1-path35" />
  <path d="M 1044 226 L 1044 266" fill="none" stroke="#0F766E" stroke-width="2" stroke-linejoin="round" marker-end="url(#as5de8cd1-arrow-0F766E)" id="as5de8cd1-path-placement-decision" />
  <text x="1054" y="251" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="600" fill="#0F766E" text-anchor="start" id="as5de8cd1-text-generates">generates</text>
  <rect x="946" y="270" width="196" height="108" rx="10" fill="#FFFFFF" stroke="#0F766E" stroke-width="1.6" stroke-dasharray="6 4" id="as5de8cd1-rect-decision" />
  <text x="962" y="298" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start" id="as5de8cd1-text-decision-title">PlacementDecision</text>
  <text x="962" y="320" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start" id="as5de8cd1-text-decision-label">decisions:</text>
  <rect x="1030" y="306" width="40" height="22" rx="6" fill="#EEF2FF" stroke="#4338CA" stroke-width="1.4" id="as5de8cd1-rect-decision-bm" />
  <text x="1050" y="321" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#4338CA" text-anchor="middle" id="as5de8cd1-text-decision-bm">bm</text>
  <rect x="1078" y="306" width="46" height="22" rx="6" fill="#EEF2FF" stroke="#4338CA" stroke-width="1.4" id="as5de8cd1-rect-decision-sno" />
  <text x="1101" y="321" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#4338CA" text-anchor="middle" id="as5de8cd1-text-decision-sno">sno</text>
  <text x="962" y="350" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="400" fill="#4B5563" text-anchor="start" id="as5de8cd1-text-decision-note1">local-cluster is not in the</text>
  <text x="962" y="366" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="400" fill="#4B5563" text-anchor="start" id="as5de8cd1-text-decision-note2">set, so it is never selected</text>
  <path
     d="M 821 226 L 821 262 L 330 262 L 330 222"
     fill="none"
     stroke="#0F766E"
     stroke-width="2"
     stroke-linejoin="round"
     marker-end="url(#as5de8cd1-arrow-0F766E)"
     id="as5de8cd1-path36" />
  <circle class="flow-dot" style="--d:0.30s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 821 226 L 821 262 L 330 262 L 330 222" begin="indefinite" data-begin="0.30" dur="2.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <rect
     x="500"
     y="252"
     width="168"
     height="20"
     fill="#FFFFFF"
     id="as5de8cd1-rect36" />
  <text
     x="584"
     y="266"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="600"
     fill="#0F766E"
     text-anchor="middle"
     id="as5de8cd1-text36">clusterSet: production</text>
  <text
     x="480"
     y="420"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="700"
     fill="#4B5563"
     text-anchor="start"
     id="as5de8cd1-text37">Cluster namespaces (one per managed cluster, created by ACM)</text>
  <rect
     x="480"
     y="432"
     width="216"
     height="182"
     rx="10"
     fill="#FFFFFF"
     stroke="#CBD5E1"
     stroke-width="1.2"
     id="as5de8cd1-rect37" />
  <text
     x="496"
     y="456"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="700"
     fill="#4338CA"
     text-anchor="start"
     id="as5de8cd1-text38">bm</text>
  <rect
     x="492"
     y="470"
     width="192"
     height="128"
     rx="10"
     fill="#FFFFFF"
     stroke="#0F766E"
     stroke-width="1.6"
     stroke-dasharray="6 4"
     id="as5de8cd1-rect38" />
  <text
     x="508"
     y="499"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15px"
     font-weight="700"
     fill="#0f766e"
     text-anchor="start"
     id="as5de8cd1-text40">Add-on</text>
  <text
     x="508"
     y="522"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="as5de8cd1-text41">ManagedClusterAddOn</text>
  <text
     x="508"
     y="538"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="as5de8cd1-text42">managed-serviceaccount,</text>
  <text
     x="508"
     y="554"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="as5de8cd1-text43">enabled by default</text>
  <rect
     x="712"
     y="432"
     width="216"
     height="182"
     rx="10"
     fill="#FFFFFF"
     stroke="#CBD5E1"
     stroke-width="1.2"
     id="as5de8cd1-rect43" />
  <text
     x="728"
     y="456"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="700"
     fill="#4338CA"
     text-anchor="start"
     id="as5de8cd1-text44">sno</text>
  <rect
     x="724"
     y="470"
     width="192"
     height="128"
     rx="10"
     fill="#FFFFFF"
     stroke="#0F766E"
     stroke-width="1.6"
     stroke-dasharray="6 4"
     id="as5de8cd1-rect44" />
  <text
     x="740"
     y="499"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15px"
     font-weight="700"
     fill="#0f766e"
     text-anchor="start"
     id="as5de8cd1-text46">Add-on</text>
  <text
     x="740"
     y="522"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="as5de8cd1-text47">ManagedClusterAddOn</text>
  <text
     x="740"
     y="538"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="as5de8cd1-text48">managed-serviceaccount,</text>
  <text
     x="740"
     y="554"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="as5de8cd1-text49">enabled by default</text>
  <rect
     x="944"
     y="432"
     width="216"
     height="182"
     rx="10"
     fill="#FFFFFF"
     stroke="#CBD5E1"
     stroke-width="1.2"
     id="as5de8cd1-rect49" />
  <text
     x="960"
     y="456"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12"
     font-weight="700"
     fill="#4338CA"
     text-anchor="start"
     id="as5de8cd1-text50">local-cluster</text>
  <rect
     x="956"
     y="470"
     width="192"
     height="128"
     rx="10"
     fill="#FFFFFF"
     stroke="#0F766E"
     stroke-width="1.6"
     stroke-dasharray="6 4"
     id="as5de8cd1-rect50" />
  <text
     x="972"
     y="499"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="15px"
     font-weight="700"
     fill="#0f766e"
     text-anchor="start"
     id="as5de8cd1-text52">Add-on</text>
  <text
     x="972"
     y="522"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="as5de8cd1-text53">ManagedClusterAddOn</text>
  <text
     x="972"
     y="538"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="as5de8cd1-text54">managed-serviceaccount,</text>
  <text
     x="972"
     y="554"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12.5"
     font-weight="400"
     fill="#4B5563"
     text-anchor="start"
     id="as5de8cd1-text55">enabled by default</text>
  <rect
     x="40"
     y="671"
     width="22"
     height="14"
     rx="3"
     fill="#E6F4F1"
     stroke="#0F766E"
     stroke-width="1.4"
     id="as5de8cd1-rect55" />
  <text
     x="70"
     y="683"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12px"
     font-weight="400"
     fill="#1f2937"
     text-anchor="start"
     id="as5de8cd1-text56">created by you</text>
  <rect
     x="166.234375"
     y="671"
     width="22"
     height="14"
     rx="3"
     fill="#FFFFFF"
     stroke="#0F766E"
     stroke-width="1.4"
     stroke-dasharray="4 3"
     id="as5de8cd1-rect56" />
  <text
     x="196.23438"
     y="683"
     font-family="Inter, 'Liberation Sans', Arial, sans-serif"
     font-size="12px"
     font-weight="400"
     fill="#1f2937"
     text-anchor="start"
     id="as5de8cd1-text57">generated by ACM</text>
  
<circle class="flow-dot" style="--d:0.00s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 212 106 L 248 140" begin="indefinite" data-begin="0.00" dur="1.20s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:0.60s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 212 210 L 248 190" begin="indefinite" data-begin="0.60" dur="1.20s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:0.90s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 1044 226 L 1044 266" begin="indefinite" data-begin="0.90" dur="0.90s" repeatCount="indefinite" calcMode="linear"/></circle>
  </svg>
</div>
  <div class="title">Objects in the hub cluster after creating the Placement</div>
</div>

</div>
<div class="paragraph">
<p>At this point nothing has happened in Argo CD yet: <em>Settings &gt; Clusters</em> still shows only <code>in-cluster</code>. The Placement only <em>decides</em> what clusters could be selected. The GitOpsCluster in the next section turns the decision into Argo CD clusters.</p>
</div>
<hr/>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_push_model">The push model</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_what_it_actually_is">What it actually is</h3>
<div class="paragraph">
<p>The push model is &#34;classic&#34; Argo CD. One Argo CD instance on the hub renders the manifests and applies them to the API server of every managed cluster, by default through the cluster proxy of the multicluster engine. ACM contributes the cluster registration, the credentials and the connection path.</p>
</div>
<div class="paragraph">
<div class="imageblock animated-svg" data-animated-svg>
  <div class="content">
<svg width="1400" height="620" viewBox="0 0 1400 620" role="img" aria-label="Push model: Placement, PlacementDecision and GitOpsCluster create a cluster secret on the hub; the ApplicationSet generates one Application per cluster. The hub Argo CD application-controller sends its API requests to the cluster-proxy on the hub, which forwards them through a tunnel opened by the managed cluster; only without the proxy does it fall back to the API server URL of the managed cluster" version="1.1" id="as4f6ec12-svg-push-model" xmlns="http://www.w3.org/2000/svg">
  <title id="as4f6ec12-title-push-model">Push model: Placement, PlacementDecision and GitOpsCluster create a cluster secret on the hub; the ApplicationSet generates one Application per cluster. The hub Argo CD application-controller sends its API requests to the cluster-proxy on the hub, which forwards them through a tunnel opened by the managed cluster; only without the proxy does it fall back to the API server URL of the managed cluster</title>
  <defs id="as4f6ec12-defs-push-model">
    <marker id="as4f6ec12-arrow-0F766E" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#0F766E" id="as4f6ec12-marker-path-0F766E" /></marker>
    <marker id="as4f6ec12-arrow-C2410C" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#C2410C" id="as4f6ec12-marker-path-C2410C" /></marker>
    <marker id="as4f6ec12-arrow-4B5563" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#4B5563" id="as4f6ec12-marker-path-4B5563" /></marker>
    </defs>
  <rect id="as4f6ec12-bg" width="1400" height="620" fill="#FFFFFF" />
  <rect id="as4f6ec12-zone-hub" x="20" y="20" width="970" height="560" rx="14" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1.5" />
  <text id="as4f6ec12-label-hub" x="40" y="48" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="700" fill="#4B5563" text-anchor="start">HUB</text>
  <rect id="as4f6ec12-zone-spoke" x="1050" y="20" width="330" height="560" rx="14" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1.5" />
  <text id="as4f6ec12-label-spoke" x="1070" y="48" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="700" fill="#4B5563" text-anchor="start">MANAGED CLUSTER  ·  bm, sno</text>
  <rect id="as4f6ec12-ns-gitops" x="40" y="60" width="730" height="480" rx="10" fill="#FFFFFF" stroke="#CBD5E1" stroke-width="1.2" />
  <text id="as4f6ec12-label-ns" x="56" y="84" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="700" fill="#C2410C" text-anchor="start">openshift-gitops</text>
  <rect id="as4f6ec12-ns-mce" x="790" y="360" width="185" height="180" rx="10" fill="#FFFFFF" stroke="#CBD5E1" stroke-width="1.2" />
  <text id="as4f6ec12-label-ns-mce" x="806" y="384" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="700" fill="#4B5563" text-anchor="start">multicluster-engine</text>
  <rect id="as4f6ec12-rect-placement" x="60" y="100" width="180" height="84" rx="10" fill="#E6F4F1" stroke="#0F766E" stroke-width="1.6" />
  <text id="as4f6ec12-title-placement" x="76" y="129" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">Placement</text>
  <text id="as4f6ec12-line0-placement" x="76" y="152" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">production-clusters</text>
  <rect id="as4f6ec12-rect-decision" x="290" y="100" width="190" height="84" rx="10" fill="#FFFFFF" stroke="#0F766E" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="as4f6ec12-title-decision" x="306" y="129" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">PlacementDecision</text>
  <text id="as4f6ec12-line0-decision" x="306" y="152" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">decisions: bm, sno</text>
  <rect id="as4f6ec12-rect-gitopscluster" x="540" y="100" width="210" height="84" rx="10" fill="#E6F4F1" stroke="#0F766E" stroke-width="1.6" />
  <text id="as4f6ec12-title-gitopscluster" x="556" y="129" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">GitOpsCluster</text>
  <text id="as4f6ec12-line0-gitopscluster" x="556" y="152" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">bridge from ACM to Argo CD</text>
  <rect id="as4f6ec12-rect-secret" x="540" y="232" width="210" height="108" rx="10" fill="#FFFFFF" stroke="#0F766E" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="as4f6ec12-title-secret" x="556" y="261" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">Cluster secret</text>
  <text id="as4f6ec12-line0-secret" x="556" y="284" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">one per cluster, token of the</text>
  <text id="as4f6ec12-line1-secret" x="556" y="300" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#4B5563" text-anchor="start">ManagedServiceAccount</text>
  <text id="as4f6ec12-line2-secret" x="556" y="316" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">application-manager</text>
  <rect id="as4f6ec12-rect-appset" x="60" y="392" width="180" height="96" rx="10" fill="#E6F4F1" stroke="#0F766E" stroke-width="1.6" />
  <text id="as4f6ec12-title-appset" x="76" y="421" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">ApplicationSet</text>
  <text id="as4f6ec12-line0-appset" x="76" y="444" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">generator:</text>
  <text id="as4f6ec12-line1-appset" x="76" y="460" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">clusterDecisionResource</text>
  <rect id="as4f6ec12-rect-application" x="290" y="392" width="190" height="96" rx="10" fill="#FFFFFF" stroke="#C2410C" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="as4f6ec12-title-application" x="306" y="421" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#C2410C" text-anchor="start">Application</text>
  <text id="as4f6ec12-line0-application" x="306" y="444" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">one per selected cluster</text>
  <rect id="as4f6ec12-rect-controller" x="540" y="392" width="210" height="96" rx="10" fill="#FDF0E8" stroke="#C2410C" stroke-width="1.6" />
  <text id="as4f6ec12-title-controller" x="556" y="421" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#C2410C" text-anchor="start">Argo CD</text>
  <text id="as4f6ec12-line0-controller" x="556" y="444" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">application-controller</text>
  <text id="as4f6ec12-line1-controller" x="556" y="460" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">reconciles all clusters</text>
  <rect id="as4f6ec12-rect-proxy" x="800" y="396" width="168" height="96" rx="10" fill="#F1F5F9" stroke="#4B5563" stroke-width="1.4" />
  <text id="as4f6ec12-title-proxy" x="816" y="425" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#4B5563" text-anchor="start">cluster-proxy</text>
  <text id="as4f6ec12-line0-proxy" x="816" y="448" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">default server URL</text>
  <text id="as4f6ec12-line1-proxy" x="816" y="464" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">in the cluster secret</text>
  <text id="as4f6ec12-note-spoke1" x="1070" y="124" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">No Argo CD on the spoke:</text>
  <text id="as4f6ec12-note-spoke2" x="1070" y="142" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">the hub reconciles everything.</text>
  <text id="as4f6ec12-note-spoke3" x="1070" y="160" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">No hub, no reconciliation.</text>
  <rect id="as4f6ec12-rect-resources" x="1070" y="232" width="290" height="108" rx="10" fill="#FFFFFF" stroke="#C2410C" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="as4f6ec12-title-resources" x="1086" y="261" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#C2410C" text-anchor="start">Resources</text>
  <text id="as4f6ec12-line0-resources" x="1086" y="284" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">applied by the hub Argo CD,</text>
  <text id="as4f6ec12-line1-resources" x="1086" y="300" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">e.g. Namespaces, Deployments</text>
  <rect id="as4f6ec12-rect-apiserver" x="1070" y="392" width="290" height="96" rx="10" fill="#FFFFFF" stroke="#4B5563" stroke-width="1.4" />
  <text id="as4f6ec12-title-apiserver" x="1086" y="421" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#4338CA" text-anchor="start">kube-apiserver</text>
  <text id="as4f6ec12-line0-apiserver" x="1086" y="444" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">of each managed cluster,</text>
  <text id="as4f6ec12-line1-apiserver" x="1086" y="460" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">cluster-proxy agent alongside</text>
  <path id="as4f6ec12-flow-placement-decision" d="M 242 142 L 286 142" fill="none" stroke="#0F766E" stroke-width="2" stroke-linejoin="round" marker-end="url(#as4f6ec12-arrow-0F766E)" />
  <path id="as4f6ec12-flow-decision-gitopscluster" d="M 482 142 L 536 142" fill="none" stroke="#0F766E" stroke-width="2" stroke-linejoin="round" marker-end="url(#as4f6ec12-arrow-0F766E)" />
  <path id="as4f6ec12-flow-gitopscluster-secret" d="M 645 186 L 645 228" fill="none" stroke="#0F766E" stroke-width="2" stroke-linejoin="round" marker-end="url(#as4f6ec12-arrow-0F766E)" />
  <text id="as4f6ec12-label-creates" x="655" y="212" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="600" fill="#0F766E" text-anchor="start">creates</text>
  <path id="as4f6ec12-flow-secret-controller" d="M 645 342 L 645 388" fill="none" stroke="#0F766E" stroke-width="2" stroke-linejoin="round" marker-end="url(#as4f6ec12-arrow-0F766E)" />
  <text id="as4f6ec12-label-usedby" x="655" y="370" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="600" fill="#0F766E" text-anchor="start">used by</text>
  <path id="as4f6ec12-flow-decision-appset" d="M 385 186 L 385 290 L 150 290 L 150 388" fill="none" stroke="#0F766E" stroke-width="2" stroke-linejoin="round" marker-end="url(#as4f6ec12-arrow-0F766E)" />
  <text id="as4f6ec12-label-reads" x="162" y="282" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="600" fill="#0F766E" text-anchor="start">reads the decisions</text>
  <path id="as4f6ec12-flow-appset-application" d="M 242 440 L 286 440" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#as4f6ec12-arrow-C2410C)" />
  <path id="as4f6ec12-flow-application-controller" d="M 482 440 L 536 440" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#as4f6ec12-arrow-C2410C)" />
  <path id="as4f6ec12-flow-controller-proxy" d="M 752 430 L 796 430" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#as4f6ec12-arrow-C2410C)" />
  <text id="as4f6ec12-label-https" x="774" y="421" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="700" fill="#C2410C" text-anchor="middle">HTTPS</text>
  <circle class="flow-dot" style="--d:0.00s" visibility="hidden" r="3.5" fill="#4B5563" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 1068 462 L 974 462" begin="indefinite" data-begin="0.00" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <rect id="as4f6ec12-bg-tunnel-label" x="974" y="472" width="96" height="30" fill="#FFFFFF" />
  <path id="as4f6ec12-flow-proxy-apiserver" d="M 970 430 L 1066 430" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#as4f6ec12-arrow-C2410C)" />
  <text id="as4f6ec12-label-requests" x="1014" y="421" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="700" fill="#C2410C" text-anchor="middle">API requests</text>
  <path id="as4f6ec12-tunnel-spoke-hub" d="M 1068 462 L 974 462" fill="none" stroke="#4B5563" stroke-width="1.6" stroke-linejoin="round" stroke-dasharray="5 3" marker-end="url(#as4f6ec12-arrow-4B5563)" />
  <text id="as4f6ec12-label-tunnel1" x="1022" y="484" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="600" fill="#4B5563" text-anchor="middle">tunnel opened</text>
  <text id="as4f6ec12-label-tunnel2" x="1022" y="498" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="600" fill="#4B5563" text-anchor="middle">by the spoke</text>
  <path id="as4f6ec12-fallback-controller-apiserver" d="M 645 490 L 645 522 L 1120 522 L 1120 492" fill="none" stroke="#4B5563" stroke-width="1.3" stroke-linejoin="round" stroke-dasharray="2 3" marker-end="url(#as4f6ec12-arrow-4B5563)" />
  <text id="as4f6ec12-label-fallback" x="635" y="516" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="600" fill="#4B5563" text-anchor="end">fallback (no proxy): API server URL</text>
  <path id="as4f6ec12-flow-apiserver-resources" d="M 1215 390 L 1215 344" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#as4f6ec12-arrow-C2410C)" />
  <text id="as4f6ec12-label-applies" x="1225" y="370" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="600" fill="#C2410C" text-anchor="start">creates</text>
  <circle id="as4f6ec12-badge-1" cx="220" cy="120" r="11" fill="#1F2937" />
  <text id="as4f6ec12-badge-1-text" x="220" y="124.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
  <circle id="as4f6ec12-badge-2" cx="730" cy="120" r="11" fill="#1F2937" />
  <text id="as4f6ec12-badge-2-text" x="730" y="124.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
  <circle id="as4f6ec12-badge-3" cx="730" cy="252" r="11" fill="#1F2937" />
  <text id="as4f6ec12-badge-3-text" x="730" y="256.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
  <circle id="as4f6ec12-badge-4" cx="952" cy="416" r="11" fill="#1F2937" />
  <text id="as4f6ec12-badge-4-text" x="952" y="420.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
  <circle id="as4f6ec12-badge-5" cx="220" cy="412" r="11" fill="#1F2937" />
  <text id="as4f6ec12-badge-5-text" x="220" y="416.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>
  <rect id="as4f6ec12-legend-you" x="40" y="594" width="22" height="14" rx="3" fill="#E6F4F1" stroke="#0F766E" stroke-width="1.6" />
  <text id="as4f6ec12-legend-you-text" x="70" y="606" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="400" fill="#1F2937" text-anchor="start">created by you</text>
  <rect id="as4f6ec12-legend-acm" x="176" y="594" width="22" height="14" rx="3" fill="#FFFFFF" stroke="#0F766E" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="as4f6ec12-legend-acm-text" x="206" y="606" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="400" fill="#1F2937" text-anchor="start">generated by ACM</text>
  <rect id="as4f6ec12-legend-argo" x="336" y="594" width="22" height="14" rx="3" fill="#FFFFFF" stroke="#C2410C" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="as4f6ec12-legend-argo-text" x="366" y="606" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="400" fill="#1F2937" text-anchor="start">generated by Argo CD</text>
  <rect id="as4f6ec12-legend-platform" x="512" y="594" width="22" height="14" rx="3" fill="#F1F5F9" stroke="#4B5563" stroke-width="1.4" />
  <text id="as4f6ec12-legend-platform-text" x="542" y="606" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="400" fill="#1F2937" text-anchor="start">platform component</text>
<circle class="flow-dot" style="--d:0.00s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 242 142 L 286 142" begin="indefinite" data-begin="0.00" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:0.40s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 482 142 L 536 142" begin="indefinite" data-begin="0.40" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:0.80s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 645 186 L 645 228" begin="indefinite" data-begin="0.80" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:1.20s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 645 342 L 645 388" begin="indefinite" data-begin="1.20" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:0.40s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 385 186 L 385 290 L 150 290 L 150 388" begin="indefinite" data-begin="0.40" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:1.20s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 242 440 L 286 440" begin="indefinite" data-begin="1.20" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:1.60s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 482 440 L 536 440" begin="indefinite" data-begin="1.60" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:2.00s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 752 430 L 796 430" begin="indefinite" data-begin="2.00" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:2.40s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 970 430 L 1066 430" begin="indefinite" data-begin="2.40" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:2.80s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 1215 390 L 1215 344" begin="indefinite" data-begin="2.80" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  </svg>
</div>
  <div class="title">Push model: the hub Argo CD applies the resources to every managed cluster</div>
</div>

</div>
<div class="paragraph">
<p>What happens under the hood (the numbers refer to the diagram above):</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>The <strong>Placement</strong> selects managed clusters, the result is stored in a <strong>PlacementDecision</strong>.</p>
</li>
<li>
<p>The <strong>GitOpsCluster</strong> controller reads the PlacementDecision and creates one Argo CD cluster secret per cluster in the Argo CD namespace, named <code>&lt;cluster-name&gt;-application-manager-cluster-secret</code>.</p>
</li>
<li>
<p>The token in the secret belongs to the <strong>ManagedServiceAccount</strong> <code>application-manager</code>. ACM rotates it automatically for you, and the cluster secret on the hub is updated when the token changes. <strong>No long-lived tokens</strong>.</p>
</li>
<li>
<p>The server URL in the cluster secret points to the <strong>cluster proxy</strong> of the multicluster engine. This is the default, because the <code>cluster-proxy</code> add-on is always enabled. Only if the cluster proxy is unavailable does ACM fall back to the API server URL of the managed cluster (OpenShift clusters only).</p>
</li>
<li>
<p>The <strong>ApplicationSet</strong> with the <code>clusterDecisionResource</code> generator creates one Application per cluster. <strong>The application-controller of the hub Argo CD reconciles all of them</strong>.</p>
</li>
</ol>
</div>
<div class="admonitionblock warning">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>By default, the <code>application-manager</code> service account has <strong>cluster administrator</strong> permissions on the managed cluster. Everybody who can read secrets in the Argo CD namespace on the hub therefore effectively has cluster-admin on every registered managed cluster. Do not hand out the <code>admin</code> role for <code>openshift-gitops</code> lightly, and consider a customised service account (see below).</p>
</div>
</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>The consequences:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>One Argo CD to rule them all.</strong> One UI, one place to look, the full resource tree and live diff for every cluster.</p>
</li>
<li>
<p><strong>The hub carries the load.</strong> Every cluster adds watches and cache to the hub’s application-controller. With many clusters, the performance tuning from <a href="/gitopscollection/2026-03-27-openshift-gitops-performance-tuning/">Ep.16</a> becomes mandatory.</p>
</li>
<li>
<p><strong>No hub, no reconciliation.</strong> If the hub is down, nothing drifts back into the desired state on the spokes.</p>
</li>
<li>
<p><strong>Network:</strong> with the cluster proxy (default), the managed cluster opens the tunnel to the hub, so the hub does not need to reach the spoke API server directly. Only with the API-URL fallback must the hub reach the spoke’s API server.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_step_1_the_gitopscluster">Step 1: The GitOpsCluster</h3>
<div class="paragraph">
<p>The GitOpsCluster connects the Placement with the Argo CD instance. This now finally creates the Argo CD clusters on the hub. In the Argo CD UI, <em>Settings &gt; Clusters</em> should show the new clusters:</p>
</div>
<div class="listingblock linenums">
<div class="title">GitOpsCluster for the push model</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">apiVersion: apps.open-cluster-management.io/v1beta1
kind: GitOpsCluster
metadata:
  name: gitops-production
  namespace: openshift-gitops
spec:
  argoServer:
    cluster: local-cluster <i class="conum" data-value="1"></i><b>(1)</b>
    argoNamespace: openshift-gitops <i class="conum" data-value="2"></i><b>(2)</b>
  placementRef:
    kind: Placement
    apiVersion: cluster.open-cluster-management.io/v1beta1
    name: production-clusters <i class="conum" data-value="3"></i><b>(3)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The name of the local cluster, where Argo CD runs. On a default hub this is <code>local-cluster</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The namespace of the Argo CD instance.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>The Placement from the prerequisites.</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>Verify that ACM created the cluster secrets:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">oc get gitopscluster gitops-production -n openshift-gitops -o yaml
oc get secrets -n openshift-gitops -l argocd.argoproj.io/secret-type=cluster</code></pre>
</div>
</div>
<div class="paragraph">
<p>You should see one <code>&lt;cluster-name&gt;-application-manager-cluster-secret</code> per selected cluster.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">NAME                                        TYPE     DATA   AGE
bm-application-manager-cluster-secret       Opaque   3      32s
cluster-kubernetes.default.svc-3396314289   Opaque   3      10h
sno-application-manager-cluster-secret      Opaque   3      32s</code></pre>
</div>
</div>
<div class="paragraph">
<p>The clusters also appear in the Argo CD UI under <em>Settings &gt; Clusters</em>.
The API URL is the cluster proxy URL:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/gitopscollection/images/ep-19/gitopscluster-cluster-secrets.png" alt="GitOpsCluster cluster secrets"/>
</div>
<div class="title">Figure 2. GitOpsCluster cluster secrets</div>
</div>
<div class="admonitionblock note">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The connection status is unknown because the cluster has no Argo CD Applications yet.
</td>
</tr>
</tbody></table>
</div>
</div>
<div class="sect2">
<h3 id="_step_2_the_applicationset">Step 2: The ApplicationSet</h3>
<div class="paragraph">
<p>For the ApplicationSet we use the <a href="https://argo-cd.readthedocs.io/en/latest/operator-manual/applicationset/Generators-Cluster-Decision-Resource/" rel="noopener" target="_blank"><strong>clusterDecisionResource</strong></a> generator. It reads the PlacementDecision directly, so the list of clusters always reflects the current state of the fleet. The generator requires a ConfigMap that tells it how to read PlacementDecisions. The GitOpsCluster controller creates it automatically under the name <code>acm-placement</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">oc get configmap acm-placement -n openshift-gitops -o yaml</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">data:
  apiVersion: cluster.open-cluster-management.io/v1beta1 <i class="conum" data-value="1"></i><b>(1)</b>
  kind: placementdecisions
  statusListKey: decisions <i class="conum" data-value="2"></i><b>(2)</b>
  matchKey: clusterName <i class="conum" data-value="3"></i><b>(3)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The PlacementDecision API.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The list in the PlacementDecision status that holds the selected clusters.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>The field in each list entry that is matched against the <code>name</code> of the Argo CD cluster secret.</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>With this information we can create the ApplicationSet using our Compliance Operator as example:</p>
</div>
<div class="listingblock linenums">
<div class="title">ApplicationSet for the push model</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: compliance-operator
  namespace: openshift-gitops
spec:
  generators:
    - clusterDecisionResource:
        configMapRef: acm-placement <i class="conum" data-value="1"></i><b>(1)</b>
        labelSelector:
          matchLabels:
            cluster.open-cluster-management.io/placement: production-clusters <i class="conum" data-value="2"></i><b>(2)</b>
        requeueAfterSeconds: 180 <i class="conum" data-value="3"></i><b>(3)</b>
  template:
    metadata:
      name: &#39;compliance-operator-{{name}}&#39; <i class="conum" data-value="4"></i><b>(4)</b>
    spec:
      project: default
      source:
        repoURL: https://github.com/tjungbauer/openshift-clusterconfig-gitops.git
        targetRevision: main
        path: &#39;clusters/{{name}}/setup-compliance-operator&#39; <i class="conum" data-value="5"></i><b>(5)</b>
      destination:
        server: &#39;{{server}}&#39; <i class="conum" data-value="6"></i><b>(6)</b>
      syncPolicy:
        automated: <i class="conum" data-value="7"></i><b>(7)</b>
          prune: true
          selfHeal: true
  syncPolicy:
    preserveResourcesOnDeletion: true <i class="conum" data-value="8"></i><b>(8)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The ConfigMap created by the GitOpsCluster controller.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The name of our Placement.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>How often the generator re-reads the PlacementDecision (in seconds).</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Create the name for the Application based on the cluster name.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>The path to the Compliance Operator setup in the Git repository. Each cluster has its own path in the Git repository.</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>The server URL from the cluster secret, usually the cluster proxy URL.</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Automated sync policy for the Application.</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td>If a cluster leaves the Placement, the generated Application is removed. With this setting, the deployed resources on that cluster are kept.</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>In Argo CD we can see the ApplicationSet which automatically created two Applications, one for each (selected) cluster:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/gitopscollection/images/ep-19/argocd-applicationset-compliance-operator.png" alt="ApplicationSet Applications"/>
</div>
<div class="title">Figure 3. ApplicationSet Applications in Argo CD</div>
</div>
<div class="paragraph">
<p>The Applications have been automatically synced by the hub Argo CD (because we configured it to do so).</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/gitopscollection/images/ep-19/argocd-applications-compliance-operator.png" alt="ApplicationSet Applications synced"/>
</div>
<div class="title">Figure 4. ApplicationSet Applications synced in Argo CD</div>
</div>
<div class="admonitionblock tip">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
The ApplicationSet created the Applications which were immediately synced by the hub Argo CD. They installed and configured the Compliance Operator on the two managed clusters: <code>bm</code> and <code>sno</code>.
</td>
</tr>
</tbody></table>
</div>
</div>
<div class="sect2">
<h3 id="_why_is_this_nice">Why is this nice?</h3>
<div class="paragraph">
<p>Compare this with manually maintained cluster secrets: no tokens, no rotation scripts. Label a new cluster with <code>vendor=OpenShift</code>, add it to the cluster set <code>production</code>, and within a few minutes it has its Application. Detach a cluster from ACM and the Application disappears. You do not need to maintain the cluster secrets manually.</p>
</div>
</div>
<div class="sect2">
<h3 id="_what_to_watch_out_for">What to watch out for</h3>
<div class="admonitionblock caution">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-caution" title="Caution"></i>
</td>
<td class="content">
Think about <code>prune: true</code> together with dynamic Placements. Without <code>preserveResourcesOnDeletion</code>, removing a cluster from the Placement (for example by changing a label) can delete the deployed resources on that cluster. Use it for everything that holds data or that the cluster cannot live without.
</td>
</tr>
</tbody></table>
</div>
</div>
<div class="sect2">
<h3 id="_least_privilege_a_customised_service_account">Least privilege: a customised service account</h3>
<div class="paragraph">
<p>For platform configuration, the ServiceAccount <code>application-manager</code> with cluster-admin is often acceptable: the Argo CD instance <em>is</em> the cluster administrator. For application teams it is too much. ACM lets you replace it with your own <strong>ManagedServiceAccount</strong> and grant it exactly the permissions you want with a <strong>ClusterPermission</strong>. Both resources are created in the namespace of the managed cluster on the hub:</p>
</div>
<div class="listingblock linenums">
<div class="title">ManagedServiceAccount and ClusterPermission for one managed cluster</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">apiVersion: authentication.open-cluster-management.io/v1alpha1
kind: ManagedServiceAccount
metadata:
  name: argocd-deployer
  namespace: sno <i class="conum" data-value="1"></i><b>(1)</b>
spec:
  rotation: {} <i class="conum" data-value="2"></i><b>(2)</b>
---
apiVersion: rbac.open-cluster-management.io/v1alpha1
kind: ClusterPermission
metadata:
  name: argocd-deployer
  namespace: sno
spec:
  roles: <i class="conum" data-value="3"></i><b>(3)</b>
    - namespace: shop
      rules:
        - apiGroups: [&#34;apps&#34;]
          resources: [&#34;deployments&#34;]
          verbs: [&#34;get&#34;, &#34;list&#34;, &#34;watch&#34;, &#34;create&#34;, &#34;update&#34;, &#34;patch&#34;, &#34;delete&#34;]
        - apiGroups: [&#34;&#34;]
          resources: [&#34;services&#34;, &#34;configmaps&#34;]
          verbs: [&#34;get&#34;, &#34;list&#34;, &#34;watch&#34;, &#34;create&#34;, &#34;update&#34;, &#34;patch&#34;, &#34;delete&#34;]
  roleBindings:
    - namespace: shop
      roleRef:
        kind: Role
      subject:
        apiGroup: authentication.open-cluster-management.io
        kind: ManagedServiceAccount
        name: argocd-deployer
  clusterRole: <i class="conum" data-value="4"></i><b>(4)</b>
    rules:
      - apiGroups: [&#34;*&#34;]
        resources: [&#34;*&#34;]
        verbs: [&#34;get&#34;, &#34;list&#34;, &#34;watch&#34;]
  clusterRoleBinding:
    subject:
      apiGroup: authentication.open-cluster-management.io
      kind: ManagedServiceAccount
      name: argocd-deployer</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The namespace of the managed cluster on the hub. The service account is created on that cluster.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Let ACM rotate the token.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Write permissions only in the namespace <code>shop</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Read-only access to the rest of the cluster, so that Argo CD can discover API resources and show health.</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>Then reference the service account in the GitOpsCluster:</p>
</div>
<div class="listingblock linenums">
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">apiVersion: apps.open-cluster-management.io/v1beta1
kind: GitOpsCluster
metadata:
  name: gitops-team-shop
  namespace: openshift-gitops
spec:
  managedServiceAccountRef: argocd-deployer <i class="conum" data-value="1"></i><b>(1)</b>
  argoServer:
    cluster: local-cluster
    argoNamespace: openshift-gitops
  placementRef:
    kind: Placement
    apiVersion: cluster.open-cluster-management.io/v1beta1
    name: production-clusters</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Use this ManagedServiceAccount instead of <code>application-manager</code>. The cluster secrets are now named <code>&lt;cluster-name&gt;-argocd-deployer-cluster-secret</code>.</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>The <code>ManagedServiceAccount</code> and the <code>ClusterPermission</code> must exist for <strong>every</strong> cluster of the Placement. Creating them by hand does not scale and is not GitOps-like.</p>
</div>
<hr/>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_pull_model">The pull model</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_what_it_actually_is_2">What it actually is</h3>
<div class="paragraph">
<p>The <strong>pull model</strong> turns the direction around. The Application is still <em>defined</em> on the hub, but the hub does not reconcile it. Instead, ACM transports the Application object itself to the managed cluster, where a <strong>local Argo CD instance</strong> does the actual work.</p>
</div>
<div class="paragraph">
<div class="imageblock animated-svg" data-animated-svg>
  <div class="content">
<svg width="1480" height="690" viewBox="0 0 1480 690" role="img" aria-label="Pull model: the ApplicationSet on the hub generates Applications that the hub Argo CD ignores. The propagation controller wraps each Application into a ManifestWork in the cluster namespace, the work agent on the managed cluster pulls it and creates the Application, and the local Argo CD reconciles it. Status returns through the ManifestWork and ACM search into the MulticlusterApplicationSetReport" version="1.1" id="asdf83d23-svg-pull-model" xmlns="http://www.w3.org/2000/svg" xmlns:c2pa="http://c2pa.org/manifest">
  <title id="asdf83d23-title-pull-model">Pull model: the ApplicationSet on the hub generates Applications that the hub Argo CD ignores. The propagation controller wraps each Application into a ManifestWork in the cluster namespace, the work agent on the managed cluster pulls it and creates the Application, and the local Argo CD reconciles it. Status returns through the ManifestWork and ACM search into the MulticlusterApplicationSetReport</title>
  <defs id="asdf83d23-defs-pull-model">
    <marker id="asdf83d23-arrow-0F766E" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#0F766E" id="asdf83d23-marker-path-0F766E" /></marker>
    <marker id="asdf83d23-arrow-C2410C" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#C2410C" id="asdf83d23-marker-path-C2410C" /></marker>
    <marker id="asdf83d23-arrow-4B5563" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#4B5563" id="asdf83d23-marker-path-4B5563" /></marker>
    </defs>
  <rect id="asdf83d23-bg" width="1480" height="690" fill="#FFFFFF" />
  <rect id="asdf83d23-zone-hub" x="20" y="20" width="1090" height="620" rx="14" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1.5" />
  <text id="asdf83d23-label-hub" x="40" y="48" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="700" fill="#4B5563" text-anchor="start">HUB</text>
  <rect id="asdf83d23-zone-spoke" x="1140" y="20" width="320" height="620" rx="14" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1.5" />
  <text id="asdf83d23-label-spoke" x="1160" y="48" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="700" fill="#4B5563" text-anchor="start">MANAGED CLUSTER  ·  bm, sno</text>
  <rect id="asdf83d23-ns-gitops" x="40" y="60" width="580" height="430" rx="10" fill="#FFFFFF" stroke="#CBD5E1" stroke-width="1.2" />
  <text id="asdf83d23-label-ns" x="56" y="84" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="700" fill="#C2410C" text-anchor="start">openshift-gitops</text>
  <rect id="asdf83d23-ns-ocm" x="640" y="60" width="260" height="430" rx="10" fill="#FFFFFF" stroke="#CBD5E1" stroke-width="1.2" />
  <text id="asdf83d23-label-ns-ocm" x="656" y="84" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="700" fill="#4B5563" text-anchor="start">open-cluster-management</text>
  <text id="asdf83d23-label-pod" x="656" y="478" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="400" fill="#4B5563" text-anchor="start">all three in the pod multicluster-integrations</text>
  <rect id="asdf83d23-ns-cluster" x="920" y="60" width="170" height="180" rx="10" fill="#FFFFFF" stroke="#CBD5E1" stroke-width="1.2" />
  <text id="asdf83d23-label-ns-cluster" x="936" y="84" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="700" fill="#4B5563" text-anchor="start">cluster namespaces</text>
  <rect id="asdf83d23-rect-appset" x="60" y="110" width="190" height="110" rx="10" fill="#E6F4F1" stroke="#0F766E" stroke-width="1.6" />
  <text id="asdf83d23-title-appset" x="76" y="139" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">ApplicationSet</text>
  <text id="asdf83d23-line0-appset" x="76" y="162" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">generator:</text>
  <text id="asdf83d23-line1-appset" x="76" y="178" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">clusterDecisionResource</text>
  <text id="asdf83d23-line2-appset" x="76" y="194" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">with pull annotations</text>
  <circle id="asdf83d23-badge-1" cx="230" cy="130" r="11" fill="#1F2937" />
  <text id="asdf83d23-badge-1-text" x="230" y="134.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
  <rect id="asdf83d23-rect-application" x="270" y="110" width="190" height="110" rx="10" fill="#FFFFFF" stroke="#C2410C" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="asdf83d23-title-application" x="286" y="139" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#C2410C" text-anchor="start">Application</text>
  <text id="asdf83d23-line0-application" x="286" y="162" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">one per selected cluster</text>
  <text id="asdf83d23-line1-application" x="286" y="178" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#4B5563" text-anchor="start">skip-reconcile: true</text>
  <text id="asdf83d23-line2-application" x="286" y="194" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">hub Argo CD ignores it</text>
  <circle id="asdf83d23-badge-2" cx="440" cy="130" r="11" fill="#1F2937" />
  <text id="asdf83d23-badge-2-text" x="440" y="134.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
  <rect id="asdf83d23-rect-report" x="300" y="250" width="305" height="96" rx="10" fill="#FFFFFF" stroke="#0F766E" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="asdf83d23-title-report" x="316" y="279" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">MulticlusterApplicationSetReport</text>
  <text id="asdf83d23-line0-report" x="316" y="302" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">sync and health per cluster,</text>
  <text id="asdf83d23-line1-report" x="316" y="318" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">aggregated, not real-time</text>
  <rect id="asdf83d23-rect-placement" x="60" y="380" width="170" height="84" rx="10" fill="#E6F4F1" stroke="#0F766E" stroke-width="1.6" />
  <text id="asdf83d23-title-placement" x="76" y="409" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">Placement</text>
  <text id="asdf83d23-line0-placement" x="76" y="432" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">production-clusters</text>
  <rect id="asdf83d23-rect-decision" x="256" y="380" width="180" height="84" rx="10" fill="#FFFFFF" stroke="#0F766E" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="asdf83d23-title-decision" x="272" y="409" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">PlacementDecision</text>
  <text id="asdf83d23-line0-decision" x="272" y="432" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">decisions: bm, sno</text>
  <rect id="asdf83d23-rect-secret" x="460" y="380" width="145" height="84" rx="10" fill="#FFFFFF" stroke="#0F766E" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="asdf83d23-title-secret" x="476" y="409" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">Cluster secret</text>
  <text id="asdf83d23-line0-secret" x="476" y="432" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">required, but</text>
  <text id="asdf83d23-line1-secret" x="476" y="448" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">not used to deploy</text>
  <rect id="asdf83d23-rect-propagation" x="656" y="110" width="228" height="110" rx="10" fill="#F1F5F9" stroke="#4B5563" stroke-width="1.4" />
  <text id="asdf83d23-title-propagation" x="672" y="139" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#4B5563" text-anchor="start">propagation</text>
  <text id="asdf83d23-line0-propagation" x="672" y="162" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">controller</text>
  <text id="asdf83d23-line1-propagation" x="672" y="178" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">wraps each Application</text>
  <text id="asdf83d23-line2-propagation" x="672" y="194" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">into a ManifestWork</text>
  <circle id="asdf83d23-badge-3" cx="864" cy="130" r="11" fill="#1F2937" />
  <text id="asdf83d23-badge-3-text" x="864" y="134.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
  <rect id="asdf83d23-rect-aggregation" x="656" y="250" width="228" height="96" rx="10" fill="#F1F5F9" stroke="#4B5563" stroke-width="1.4" />
  <text id="asdf83d23-title-aggregation" x="672" y="279" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#4B5563" text-anchor="start">aggregation</text>
  <text id="asdf83d23-line0-aggregation" x="672" y="302" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">controller</text>
  <text id="asdf83d23-line1-aggregation" x="672" y="318" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">builds the report</text>
  <circle id="asdf83d23-badge-6" cx="864" cy="270" r="11" fill="#1F2937" />
  <text id="asdf83d23-badge-6-text" x="864" y="274.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">6</text>
  <rect id="asdf83d23-rect-sync" x="656" y="370" width="228" height="96" rx="10" fill="#F1F5F9" stroke="#4B5563" stroke-width="1.4" />
  <text id="asdf83d23-title-sync" x="672" y="399" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#4B5563" text-anchor="start">resource sync</text>
  <text id="asdf83d23-line0-sync" x="672" y="422" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">controller</text>
  <text id="asdf83d23-line1-sync" x="672" y="438" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">queries ACM search, 10 s</text>
  <rect id="asdf83d23-rect-manifestwork" x="936" y="110" width="138" height="110" rx="10" fill="#FFFFFF" stroke="#0F766E" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="asdf83d23-title-manifestwork" x="952" y="139" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">ManifestWork</text>
  <text id="asdf83d23-line0-manifestwork" x="952" y="162" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">in namespace</text>
  <text id="asdf83d23-line1-manifestwork" x="952" y="178" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#4B5563" text-anchor="start">sno or bm</text>
  <text id="asdf83d23-line2-manifestwork" x="952" y="194" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">wraps the App</text>
  <rect id="asdf83d23-rect-agent" x="1160" y="110" width="280" height="110" rx="10" fill="#F1F5F9" stroke="#4B5563" stroke-width="1.4" />
  <text id="asdf83d23-title-agent" x="1176" y="139" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#4B5563" text-anchor="start">work agent</text>
  <text id="asdf83d23-line0-agent" x="1176" y="162" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">part of the klusterlet,</text>
  <text id="asdf83d23-line1-agent" x="1176" y="178" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">creates the Application</text>
  <text id="asdf83d23-line2-agent" x="1176" y="194" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">from the ManifestWork</text>
  <circle id="asdf83d23-badge-4" cx="1420" cy="130" r="11" fill="#1F2937" />
  <text id="asdf83d23-badge-4-text" x="1420" y="134.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
  <rect id="asdf83d23-rect-spoke-app" x="1160" y="250" width="280" height="84" rx="10" fill="#FFFFFF" stroke="#C2410C" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="asdf83d23-title-spoke-app" x="1176" y="279" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#C2410C" text-anchor="start">Application</text>
  <text id="asdf83d23-line0-spoke-app" x="1176" y="302" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">in openshift-gitops on the spoke</text>
  <rect id="asdf83d23-rect-local-argo" x="1160" y="370" width="280" height="96" rx="10" fill="#FDF0E8" stroke="#C2410C" stroke-width="1.6" />
  <text id="asdf83d23-title-local-argo" x="1176" y="399" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#C2410C" text-anchor="start">Local Argo CD</text>
  <text id="asdf83d23-line0-local-argo" x="1176" y="422" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">application-controller</text>
  <text id="asdf83d23-line1-local-argo" x="1176" y="438" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">pulls Git, reconciles locally</text>
  <circle id="asdf83d23-badge-5" cx="1420" cy="390" r="11" fill="#1F2937" />
  <text id="asdf83d23-badge-5-text" x="1420" y="394.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>
  <rect id="asdf83d23-rect-resources" x="1160" y="500" width="280" height="84" rx="10" fill="#FFFFFF" stroke="#C2410C" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="asdf83d23-title-resources" x="1176" y="529" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#C2410C" text-anchor="start">Resources</text>
  <text id="asdf83d23-line0-resources" x="1176" y="552" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">applied by the local Argo CD</text>
  <path id="asdf83d23-flow-placement-decision" d="M 232 422 L 252 422" fill="none" stroke="#0F766E" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdf83d23-arrow-0F766E)" />
  <path id="asdf83d23-flow-decision-appset" d="M 345 378 L 345 362 L 150 362 L 150 224" fill="none" stroke="#0F766E" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdf83d23-arrow-0F766E)" />
  <text id="asdf83d23-label-reads" x="160" y="356" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="600" fill="#0F766E" text-anchor="start">reads the decisions</text>
  <path id="asdf83d23-flow-appset-application" d="M 252 165 L 266 165" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdf83d23-arrow-C2410C)" />
  <path id="asdf83d23-flow-application-propagation" d="M 462 165 L 652 165" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdf83d23-arrow-C2410C)" />
  <text id="asdf83d23-label-watches" x="557" y="156" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="700" fill="#C2410C" text-anchor="middle">ocm-managed-cluster</text>
  <path id="asdf83d23-flow-propagation-manifestwork" d="M 886 165 L 932 165" fill="none" stroke="#0F766E" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdf83d23-arrow-0F766E)" />
  <circle class="flow-dot" style="--d:0.00s" visibility="hidden" r="3.5" fill="#4B5563" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 1158 180 L 1078 180" begin="indefinite" data-begin="0.00" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <rect id="asdf83d23-bg-pull-label" x="1092" y="190" width="60" height="32" rx="0" fill="#F8FAFC" stroke="#F8FAFC" stroke-width="0" />
  <path id="asdf83d23-flow-manifestwork-agent" d="M 1076 150 L 1156 150" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdf83d23-arrow-C2410C)" />
  <text id="asdf83d23-label-app" x="1116" y="141" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="700" fill="#C2410C" text-anchor="middle">Application</text>
  <path id="asdf83d23-pull-spoke-hub" d="M 1158 180 L 1078 180" fill="none" stroke="#4B5563" stroke-width="1.6" stroke-linejoin="round" stroke-dasharray="5 3" marker-end="url(#asdf83d23-arrow-4B5563)" />
  <text id="asdf83d23-label-pull1" x="1118" y="202" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="600" fill="#4B5563" text-anchor="middle">pulled by</text>
  <text id="asdf83d23-label-pull2" x="1118" y="216" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="600" fill="#4B5563" text-anchor="middle">the spoke</text>
  <path id="asdf83d23-flow-agent-app" d="M 1300 222 L 1300 246" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdf83d23-arrow-C2410C)" />
  <path id="asdf83d23-flow-app-argo" d="M 1300 336 L 1300 366" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdf83d23-arrow-C2410C)" />
  <path id="asdf83d23-flow-argo-resources" d="M 1300 468 L 1300 496" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdf83d23-arrow-C2410C)" />
  <text id="asdf83d23-label-applies" x="1310" y="488" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="600" fill="#C2410C" text-anchor="start">applies</text>
  <path id="asdf83d23-status-manifestwork" d="M 1005 222 L 1005 298 L 888 298" fill="none" stroke="#0F766E" stroke-width="1.6" stroke-linejoin="round" stroke-dasharray="5 3" marker-end="url(#asdf83d23-arrow-0F766E)" />
  <text id="asdf83d23-label-mw-status" x="1015" y="270" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="600" fill="#0F766E" text-anchor="start">ManifestWork</text>
  <text id="asdf83d23-label-mw-status2" x="1015" y="284" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="600" fill="#0F766E" text-anchor="start">status</text>
  <path id="asdf83d23-status-search" d="M 1156 418 L 888 418" fill="none" stroke="#0F766E" stroke-width="1.6" stroke-linejoin="round" stroke-dasharray="5 3" marker-end="url(#asdf83d23-arrow-0F766E)" />
  <text id="asdf83d23-label-search" x="1022" y="409" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="600" fill="#0F766E" text-anchor="middle">ACM search: resources, errors</text>
  <path id="asdf83d23-status-sync-aggregation" d="M 770 368 L 770 350" fill="none" stroke="#0F766E" stroke-width="1.6" stroke-linejoin="round" stroke-dasharray="5 3" marker-end="url(#asdf83d23-arrow-0F766E)" />
  <path id="asdf83d23-status-aggregation-report" d="M 654 298 L 609 298" fill="none" stroke="#0F766E" stroke-width="1.6" stroke-linejoin="round" stroke-dasharray="5 3" marker-end="url(#asdf83d23-arrow-0F766E)" />
  <text id="asdf83d23-note-hub1" x="40" y="530" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">The hub Argo CD only generates the Applications:</text>
  <text id="asdf83d23-note-hub2" x="40" y="548" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">reconciliation happens on every spoke, with its own OpenShift GitOps.</text>
  <text id="asdf83d23-note-hub3" x="40" y="566" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">No hub, still reconciliation. All connections are opened by the spoke.</text>
  <text id="asdf83d23-note-hub4" x="40" y="594" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="600" fill="#0F766E" text-anchor="start">Dashed arrows: status back to the hub.</text>
  <rect id="asdf83d23-legend-you" x="40" y="664" width="22" height="14" rx="3" fill="#E6F4F1" stroke="#0F766E" stroke-width="1.6" />
  <text id="asdf83d23-legend-you-text" x="70" y="676" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="400" fill="#1F2937" text-anchor="start">created by you</text>
  <rect id="asdf83d23-legend-acm" x="194.609375" y="664" width="22" height="14" rx="3" fill="#FFFFFF" stroke="#0F766E" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="asdf83d23-legend-acm-text" x="224.609375" y="676" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="400" fill="#1F2937" text-anchor="start">generated by ACM</text>
  <rect id="asdf83d23-legend-argo" x="370.5625" y="664" width="22" height="14" rx="3" fill="#FFFFFF" stroke="#C2410C" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="asdf83d23-legend-argo-text" x="400.5625" y="676" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="400" fill="#1F2937" text-anchor="start">generated by Argo CD</text>
  <rect id="asdf83d23-legend-platform" x="566.984375" y="664" width="22" height="14" rx="3" fill="#F1F5F9" stroke="#4B5563" stroke-width="1.6" />
  <text id="asdf83d23-legend-platform-text" x="596.984375" y="676" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="400" fill="#1F2937" text-anchor="start">platform component</text>
<circle class="flow-dot" style="--d:0.00s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 232 422 L 252 422" begin="indefinite" data-begin="0.00" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:0.40s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 345 378 L 345 362 L 150 362 L 150 224" begin="indefinite" data-begin="0.40" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:0.80s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 252 165 L 266 165" begin="indefinite" data-begin="0.80" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:1.20s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 462 165 L 652 165" begin="indefinite" data-begin="1.20" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:1.60s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 886 165 L 932 165" begin="indefinite" data-begin="1.60" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:2.00s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 1076 150 L 1156 150" begin="indefinite" data-begin="2.00" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:2.40s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 1300 222 L 1300 246" begin="indefinite" data-begin="2.40" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:2.80s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 1300 336 L 1300 366" begin="indefinite" data-begin="2.80" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:3.20s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 1300 468 L 1300 496" begin="indefinite" data-begin="3.20" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:3.60s" visibility="hidden" r="3.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 1005 222 L 1005 298 L 888 298" begin="indefinite" data-begin="3.60" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:3.60s" visibility="hidden" r="3.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 1156 418 L 888 418" begin="indefinite" data-begin="3.60" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:4.00s" visibility="hidden" r="3.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 770 368 L 770 350" begin="indefinite" data-begin="4.00" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:4.40s" visibility="hidden" r="3.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 654 298 L 609 298" begin="indefinite" data-begin="4.40" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  </svg>
</div>
  <div class="title">Pull model: the hub ships the Application, the local Argo CD reconciles it</div>
</div>

</div>
<div class="paragraph">
<p>What happens under the hood:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>The ApplicationSet on the hub generates an Application per cluster, just as in the push model.</p>
</li>
<li>
<p>The generated Application carries the label <code>apps.open-cluster-management.io/pull-to-ocm-managed-cluster: &#34;true&#34;</code> and the annotation <code>argocd.argoproj.io/skip-reconcile: &#34;true&#34;</code>. The latter tells the hub Argo CD to ignore it.</p>
</li>
<li>
<p>The <strong>propagation controller</strong> on the hub picks up every Application with the annotation <code>apps.open-cluster-management.io/ocm-managed-cluster</code>. It wraps the Application into a <strong>ManifestWork</strong> in the namespace of the target cluster. We are still on the hub cluster at this point.</p>
</li>
<li>
<p>The <strong>work agent</strong> on the managed cluster (part of the regular ACM klusterlet) pulls the ManifestWork and creates the Application in the <code>openshift-gitops</code> namespace of the managed cluster.</p>
</li>
<li>
<p>The <strong>local Argo CD on the managed cluster</strong> pulls Git and reconciles the Application against its own API server.</p>
</li>
<li>
<p>The status travels back in two ways: the ManifestWork reports the status of the Application object, and the <strong>resource sync controller</strong> queries the ACM search component on each managed cluster for the resource list and error messages. The <strong>aggregation controller</strong> combines both into a <strong>MulticlusterApplicationSetReport</strong> per ApplicationSet.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>All three controllers run as containers in the <code>multicluster-integrations</code> pod in the <code>open-cluster-management</code> namespace on the hub. The resource sync and aggregation controllers run every 10 seconds.</p>
</div>
<div class="paragraph">
<p>The consequences:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>The spokes do the work.</strong> The hub only creates ManifestWorks, which scales much better than one controller watching every cluster.</p>
</li>
<li>
<p><strong>The hub never connects to the spoke APIs.</strong> All connections are initiated by the managed cluster (the klusterlet of ACM does that anyway). Perfect for firewalls that only allow outbound traffic from the spoke.</p>
</li>
<li>
<p><strong>No hub, still reconciliation.</strong> If the hub goes away, the local Argo CD keeps self-healing with the last known Application.</p>
</li>
<li>
<p><strong>Limited visibility.</strong> The status on the hub is not real-time and not complete. Resources that failed to deploy are not part of the resource list, only the error messages show them. For details, you have to look at the Argo CD of the spoke.</p>
</li>
<li>
<p><strong>An Argo CD per cluster.</strong> Every managed cluster runs a full OpenShift GitOps instance, needs access to Git and its own repository credentials, and must be installed, updated and given the right permissions.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The ACM documentation lists the requirements for the pull model:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>OpenShift GitOps installed on the hub <strong>and</strong> on every target managed cluster, in the namespace <code>openshift-gitops</code>.</p>
</li>
<li>
<p>OpenShift GitOps on the hub in version 1.9.0 or later, and <strong>equal to or newer than</strong> the version on the managed clusters.</p>
</li>
<li>
<p>A cluster secret per managed cluster on the hub, created by a GitOpsCluster. The ApplicationSet generator matches the PlacementDecision against these secrets, even if the hub never uses them to deploy.</p>
</li>
</ul>
</div>
<div class="admonitionblock warning">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
So the GitOpsCluster and the Placement from the push model are needed here as well.
</td>
</tr>
</tbody></table>
</div>
</div>
<div class="sect2">
<h3 id="_step_1_openshift_gitops_on_the_managed_clusters">Step 1: OpenShift GitOps on the managed clusters</h3>
<div class="paragraph">
<p>There are two ways to get a local Argo CD onto every spoke.</p>
</div>
<div class="sect3">
<h4 id="_option_a_the_gitops_add_on_in_basic_mode">Option A: the GitOps add-on in Basic mode</h4>
<div class="paragraph">
<p>The GitOpsCluster can install OpenShift GitOps automatically for you. Simply enable the add-on:</p>
</div>
<div class="listingblock linenums">
<div class="title">GitOpsCluster with the GitOps add-on in Basic mode</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">apiVersion: apps.open-cluster-management.io/v1beta1
kind: GitOpsCluster
metadata:
  name: gitops-production
  namespace: openshift-gitops
spec:
  argoServer:
    argoNamespace: openshift-gitops
  placementRef:
    kind: Placement
    apiVersion: cluster.open-cluster-management.io/v1beta1
    name: production-clusters
    namespace: openshift-gitops
  gitopsAddon:
    enabled: true <i class="conum" data-value="1"></i><b>(1)</b>
    overrideExistingConfigs: false <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Basic mode: deploys the OpenShift GitOps Operator into <code>openshift-gitops-operator</code> and an Argo CD instance into <code>openshift-gitops</code> on every selected cluster.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Safety feature: do not overwrite existing Argo CD configurations on the managed clusters.</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>When we update the GitOpsCluster resource, that we have created in the previous chapter, the managed clusters will start to install OpenShift GitOps. The Argo CD instance <code>acm-openshift-gitops</code> will be created in the namespace <code>openshift-gitops</code>.</p>
</div>
<div class="admonitionblock tip">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
This is by far the easiest way to get a local Argo CD onto every spoke as you need to configure the GitOpsCluster resource anyway. This automatically creates an ACM policy <code>gitops-production-argocd-policy</code> that manages the Argo CD resource on the spokes.
</td>
</tr>
</tbody></table>
</div>
<div class="admonitionblock warning">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="ulist">
<ul>
<li>
<p>Enabling the GitOps add-on through a GitOpsCluster <strong>disables the push model for all applications</strong>. This is a fleet-wide decision. If you need push and pull side by side, use option B.</p>
</li>
<li>
<p>The Basic mode is supported for <strong>OpenShift</strong> managed clusters only.</p>
</li>
<li>
<p>To customise the ArgoCD resource on a single spoke without the add-on reverting it, annotate it with <code>gitops-addon.open-cluster-management.io/skip: &#34;true&#34;</code>.</p>
</li>
</ul>
</div>
</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>Verify the add-on:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash"># on a managed cluster
oc get pods -n open-cluster-management-agent-addon
oc get pods -n openshift-gitops-operator
oc get pods -n openshift-gitops</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_option_b_your_own_policy">Option B: your own policy</h4>
<div class="paragraph">
<p>If you want better control over the operator version, or you need the push model as well, install OpenShift GitOps with your <strong>own policy</strong>. This is the same OperatorPolicy pattern as in <a href="/gitopscollection/2026-10-20-openshift-gitops-vs-acm/#_option_2_acm_with_an_operatorpolicy">Ep.18</a>:</p>
</div>
<div class="listingblock linenums">
<div class="title">Policy that installs OpenShift GitOps on the managed clusters</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
  name: openshift-gitops-spoke
  namespace: acm-policies
spec:
  remediationAction: enforce <i class="conum" data-value="1"></i><b>(1)</b>
  disabled: false
  policy-templates:
    - objectDefinition: <i class="conum" data-value="2"></i><b>(2)</b>
        apiVersion: policy.open-cluster-management.io/v1
        kind: ConfigurationPolicy
        metadata:
          name: openshift-gitops-operator-namespace
        spec:
          severity: high
          object-templates:
            - complianceType: musthave
              objectDefinition:
                apiVersion: v1
                kind: Namespace
                metadata:
                  name: openshift-gitops-operator
    - objectDefinition: <i class="conum" data-value="3"></i><b>(3)</b>
        apiVersion: policy.open-cluster-management.io/v1beta1
        kind: OperatorPolicy
        metadata:
          name: openshift-gitops-operator
        spec:
          severity: high
          complianceType: musthave
          subscription:
            name: openshift-gitops-operator
            namespace: openshift-gitops-operator
            channel: gitops-1.22 <i class="conum" data-value="4"></i><b>(4)</b>
            source: redhat-operators
            sourceNamespace: openshift-marketplace
          upgradeApproval: Automatic</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Enforce the policy.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Create the namespace <code>openshift-gitops-operator</code> on the managed clusters.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Install the OpenShift GitOps Operator.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Pin the channel. The hub must run the same or a newer OpenShift GitOps version than the spokes, so upgrade the hub first.</td>
</tr>
</tbody></table>
</div>
<div class="admonitionblock note">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
This installs the Namespace and the Operator, the Argo CD instance is created automatically by the Operator.
</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>Bind the policy with a Placement and a PlacementBinding in <code>acm-policies</code>, using the same label selector as the GitOps Placement, so that every cluster that should receive pull Applications also receives a local Argo CD. See above for examples.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_2_permissions_for_the_local_argo_cd">Step 2: Permissions for the local Argo CD</h3>
<div class="paragraph">
<p>Now with Argo CD deployed on the managed cluster, the local application-controller must be allowed to create whatever the Applications contain. Without that, syncs fail with <code>forbidden</code> errors for the service account <code>openshift-gitops-argocd-application-controller</code>. The ACM documentation offers two ways:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Cluster administrator</strong>: a ClusterRoleBinding on every managed cluster. Simple, and for platform configuration usually what you want. For example, to install a new Operator.</p>
</li>
<li>
<p><strong>Least privilege</strong>: create every destination namespace on the managed cluster with the label <code>argocd.argoproj.io/managed-by: openshift-gitops</code>. The OpenShift GitOps Operator then grants the local instance admin rights in exactly these namespaces. The namespaces must be declared in Git, as part of the Application.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>For option 1, add the ClusterRoleBinding to the policy that manages the spokes (the <code>&lt;gitopscluster-name&gt;-argocd-policy</code> from option A or your own policy from option B):</p>
</div>
<div class="listingblock linenums">
<div class="title">Policy template granting the local controller cluster-admin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">    - objectDefinition:
        apiVersion: policy.open-cluster-management.io/v1
        kind: ConfigurationPolicy
        metadata:
          name: openshift-gitops-controller-rbac
        spec:
          severity: high
          object-templates:
            - complianceType: musthave
              objectDefinition:
                apiVersion: rbac.authorization.k8s.io/v1
                kind: ClusterRoleBinding
                metadata:
                  name: argo-admin
                subjects:
                  - kind: ServiceAccount
                    name: openshift-gitops-argocd-application-controller
                    namespace: openshift-gitops
                roleRef:
                  apiGroup: rbac.authorization.k8s.io
                  kind: ClusterRole
                  name: cluster-admin</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_3_the_applicationset_for_the_pull_model">Step 3: The ApplicationSet for the pull model</h3>
<div class="paragraph">
<p>The ApplicationSet uses the same generator as the push model: <code>clusterDecisionResource</code>. The template gets a label and three annotations, and the destination changes.</p>
</div>
<div class="admonitionblock note">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The Compliance Operator is already installed on <code>sno</code> and <code>bm</code> by the push model. To keep the two models from fighting over the same objects, the pull example deploys something else: the console branding from the folder <a href="https://github.com/tjungbauer/openshift-clusterconfig-gitops/tree/main/clusters/management-cluster/branding" rel="noopener" target="_blank">clusters/&lt;cluster&gt;/branding</a> of my GitOps repository.
</td>
</tr>
</tbody></table>
</div>
<div class="listingblock linenums">
<div class="title">ApplicationSet for the pull model</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: branding-pull
  namespace: openshift-gitops
spec:
  generators:
    - clusterDecisionResource:
        configMapRef: acm-placement
        labelSelector:
          matchLabels:
            cluster.open-cluster-management.io/placement: production-clusters
        requeueAfterSeconds: 180
  template:
    metadata:
      name: &#39;branding-{{name}}&#39;
      labels:
        apps.open-cluster-management.io/pull-to-ocm-managed-cluster: &#39;true&#39; <i class="conum" data-value="1"></i><b>(1)</b>
      annotations:
        argocd.argoproj.io/skip-reconcile: &#39;true&#39; <i class="conum" data-value="2"></i><b>(2)</b>
        apps.open-cluster-management.io/ocm-managed-cluster: &#39;{{name}}&#39; <i class="conum" data-value="3"></i><b>(3)</b>
        apps.open-cluster-management.io/ocm-managed-cluster-app-namespace: openshift-gitops <i class="conum" data-value="4"></i><b>(4)</b>
    spec:
      project: default <i class="conum" data-value="5"></i><b>(5)</b>
      source:
        repoURL: https://github.com/tjungbauer/openshift-clusterconfig-gitops.git
        targetRevision: main
        path: &#39;clusters/{{name}}/branding&#39; <i class="conum" data-value="6"></i><b>(6)</b>
      destination:
        server: https://kubernetes.default.svc <i class="conum" data-value="7"></i><b>(7)</b>
      syncPolicy:
        automated:
          prune: true
          selfHeal: true</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Marks the Application as a pull model Application.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Prevents the Argo CD instance on the hub from reconciling the Application itself.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>The target managed cluster. The propagation controller only processes Applications with this annotation.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>The namespace on the managed cluster where the Application is created, i.e. the namespace of the local Argo CD.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>The AppProject must exist <strong>on the managed cluster</strong>. The default instance always has <code>default</code>. Custom projects, and the repository credentials for private repositories, have to be created on the spokes, for example by another policy.</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>One folder per cluster: <code>clusters/sno/branding</code> and <code>clusters/bm/branding</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>From the perspective of the local Argo CD on the spoke, the destination is the cluster it runs on.</td>
</tr>
</tbody></table>
</div>
<div class="admonitionblock caution">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-caution" title="Caution"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Do not forget the <code>skip-reconcile</code> annotation. Without it, the hub Argo CD tries to sync the Application to <code><a class="bare" href="https://kubernetes.default.svc">https://kubernetes.default.svc</a></code>, which is the <strong>hub</strong> itself. You would deploy the configuration of every managed cluster onto the hub.</p>
</div>
</td>
</tr>
</tbody></table>
</div>
</div>
<div class="sect2">
<h3 id="_step_4_verify_the_flow">Step 4: Verify the flow</h3>
<div class="paragraph">
<p>Follow the Application through the chain. First, the Applications on the hub, which are not reconciled there:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">oc get applications -n openshift-gitops -l apps.open-cluster-management.io/pull-to-ocm-managed-cluster=true</code></pre>
</div>
</div>
<div class="paragraph">
<p>Then the ManifestWork the propagation controller created in the namespace of one managed cluster:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">oc get manifestwork -n sno</code></pre>
</div>
</div>
<div class="paragraph">
<p>On the managed cluster, the Application now exists and is reconciled by the local Argo CD:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">oc get applications -n openshift-gitops</code></pre>
</div>
</div>
<div class="paragraph">
<p>Back on the hub, the aggregated status per ApplicationSet:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">oc get multiclusterapplicationsetreports -n openshift-gitops
oc get multiclusterapplicationsetreport branding-pull -n openshift-gitops -o yaml</code></pre>
</div>
</div>
<div class="paragraph">
<p>The report lists the sync and health status per cluster and a summary. A shortened example with one failing cluster:</p>
</div>
<div class="listingblock linenums">
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">apiVersion: apps.open-cluster-management.io/v1alpha1
kind: MulticlusterApplicationSetReport
metadata:
  name: branding-pull
  namespace: openshift-gitops
statuses:
  clusterConditions:
    - cluster: sno
      healthStatus: Healthy
      syncStatus: Synced
    - cluster: bm
      healthStatus: Missing
      syncStatus: OutOfSync
      conditions:
        - type: SyncError
          message: &#39;Failed sync attempt: ... is forbidden: User &#34;system:serviceaccount:openshift-gitops:openshift-gitops-argocd-application-controller&#34; cannot create resource ...&#39;
  summary:
    clusters: &#34;2&#34;
    healthy: &#34;1&#34;
    notHealthy: &#34;1&#34;
    synced: &#34;1&#34;
    notSynced: &#34;1&#34;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>forbidden</code> message in the example is the most common error with the pull model: the local controller is missing permissions (step 2). The report is the first place to look. For the details, go to the Argo CD of the spoke: it has the full conditions and events.</p>
</div>
</div>
<div class="sect2">
<h3 id="_the_visibility_trade_off">The visibility trade-off</h3>
<div class="paragraph">
<p>The pull model has one weak spot: <strong>visibility</strong>. The hub only gets an aggregated and delayed status. The hub cluster never connects directly to the spoke clusters and therefore never sees the full resource tree.</p>
</div>
<div class="paragraph">
<p>The result is the following:</p>
</div>
<div class="paragraph">
<p>While the ApplicationSet in the hub shows a healthy sync status:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/gitopscollection/images/ep-19/pull-argocd-applicationset-branding-pull.png" alt="Pull Model ApplicationSet"/>
</div>
<div class="title">Figure 5. ApplicationSet in the hub shows a healthy sync status</div>
</div>
<div class="paragraph">
<p>The Application itself shows also a healthy sync status BUT …​ it does not show any child-resources.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/gitopscollection/images/ep-19/pull-argocd-application-branding-bm.png" alt="Pull Model UI Trade-off"/>
</div>
<div class="title">Figure 6. Pull Model UI Trade-off</div>
</div>
<div class="admonitionblock caution">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-caution" title="Caution"></i>
</td>
<td class="content">
The whole resource tree is missing in the UI. You are basically blind and you cannot see a Diff. You can check <code>oc get multiclusterapplicationsetreport branding-pull -n openshift-gitops -o yaml</code> to see the full status, but that’s not as nice as seeing it in the UI.
</td>
</tr>
</tbody></table>
</div>
</div>
<div class="sect2">
<h3 id="_mixing_push_and_pull">Mixing push and pull</h3>
<div class="paragraph">
<p>The model is decided <strong>per ApplicationSet</strong>, by the label and annotations in the template. One hub Argo CD can therefore use push and pull ApplicationSets side by side. A pattern I like: platform components via push (one UI, full visibility for a small platform team), application workloads via pull.</p>
</div>
<div class="paragraph">
<p>The one exception is the GitOps add-on: once it is enabled through a GitOpsCluster, the push model is switched off. If you want to mix, install OpenShift GitOps on the spokes with your own policy (option B).</p>
</div>
<hr/>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_argo_cd_agent_via_the_gitops_add_on">The Argo CD Agent via the GitOps add-on</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_what_it_actually_is_3">What it actually is</h3>
<div class="paragraph">
<p>The pull model has one weak spot: visibility. The hub only gets an aggregated, delayed status. The Argo CD Agent (see <a href="/gitopscollection/2026-01-14-argocd-agent/">Ep.15</a>) solves exactly that. A lightweight agent on the spoke reconciles locally and synchronises the full Application status back to the <strong>principal</strong> on the hub. The connection is initiated by the spoke (gRPC with mutual TLS), the hub holds no Kubernetes credentials for the spokes, and the Argo CD UI on the hub shows the full resource tree.</p>
</div>
<div class="paragraph">
<p>Setting up the agent by hand means a lot of plumbing: a CA, certificates for the principal and for every agent, registering every agent, installing the agent on every spoke. This is where ACM helps. The GitOps add-on in <strong>Agent mode</strong> does all of that for the clusters of a Placement: it installs OpenShift GitOps and the agent on the spokes, manages the PKI and creates the cluster secrets in agent mode.</p>
</div>
<div class="paragraph">
<div class="imageblock animated-svg" data-animated-svg>
  <div class="content">
<svg width="1400" height="780" viewBox="0 0 1400 780" role="img" aria-label="Argo CD Agent via the ACM GitOps add-on: the hub Argo CD runs as principal with its controller disabled. The GitOpsCluster creates certificates, agent-mode cluster secrets and ManifestWorks; the gitops-addon on the managed cluster installs OpenShift GitOps and the agent. The agent opens a gRPC connection with mutual TLS to the principal, receives the Applications routed by destination name, reconciles them locally and sends the full status back, so the hub UI shows the complete resource tree" version="1.1" id="asdcaeff4-svg-agent-model" xmlns="http://www.w3.org/2000/svg" xmlns:c2pa="http://c2pa.org/manifest">
  <title id="asdcaeff4-title-agent-model">Argo CD Agent via the ACM GitOps add-on: the hub Argo CD runs as principal with its controller disabled. The GitOpsCluster creates certificates, agent-mode cluster secrets and ManifestWorks; the gitops-addon on the managed cluster installs OpenShift GitOps and the agent. The agent opens a gRPC connection with mutual TLS to the principal, receives the Applications routed by destination name, reconciles them locally and sends the full status back, so the hub UI shows the complete resource tree</title>
  <defs id="asdcaeff4-defs-agent-model">
    <marker id="asdcaeff4-arrow-0F766E" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#0F766E" id="asdcaeff4-marker-path-0F766E" /></marker>
    <marker id="asdcaeff4-arrow-C2410C" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#C2410C" id="asdcaeff4-marker-path-C2410C" /></marker>
    <marker id="asdcaeff4-arrow-4B5563" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#4B5563" id="asdcaeff4-marker-path-4B5563" /></marker>
    </defs>
  <rect id="asdcaeff4-bg" width="1400" height="780" fill="#FFFFFF" />
  <rect id="asdcaeff4-zone-hub" x="20" y="20" width="1360" height="360" rx="14" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1.5" />
  <text id="asdcaeff4-label-hub" x="40" y="48" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="700" fill="#4B5563" text-anchor="start">HUB</text>
  <rect id="asdcaeff4-zone-spoke" x="20" y="450" width="1360" height="280" rx="14" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1.5" />
  <text id="asdcaeff4-label-spoke" x="40" y="478" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="700" fill="#4B5563" text-anchor="start">MANAGED CLUSTER  ·  bm, sno</text>
  <rect id="asdcaeff4-ns-gitops" x="40" y="60" width="1020" height="300" rx="10" fill="#FFFFFF" stroke="#CBD5E1" stroke-width="1.2" />
  <text id="asdcaeff4-label-ns" x="56" y="84" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="700" fill="#C2410C" text-anchor="start">openshift-gitops</text>
  <rect id="asdcaeff4-ns-cluster" x="1080" y="60" width="280" height="300" rx="10" fill="#FFFFFF" stroke="#CBD5E1" stroke-width="1.2" />
  <text id="asdcaeff4-label-ns-cluster" x="1096" y="84" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="700" fill="#4B5563" text-anchor="start">cluster namespaces</text>
  <rect id="asdcaeff4-rect-placement" x="60" y="100" width="180" height="100" rx="10" fill="#E6F4F1" stroke="#0F766E" stroke-width="1.6" />
  <text id="asdcaeff4-title-placement" x="76" y="129" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">Placement</text>
  <text id="asdcaeff4-line0-placement" x="76" y="152" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">production-clusters</text>
  <rect id="asdcaeff4-rect-gitopscluster" x="260" y="100" width="230" height="100" rx="10" fill="#E6F4F1" stroke="#0F766E" stroke-width="1.6" />
  <text id="asdcaeff4-title-gitopscluster" x="276" y="129" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">GitOpsCluster</text>
  <text id="asdcaeff4-line0-gitopscluster" x="276" y="152" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">gitopsAddon + argoCDAgent</text>
  <text id="asdcaeff4-line1-gitopscluster" x="276" y="168" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">mode: managed</text>
  <circle id="asdcaeff4-badge-2" cx="470" cy="120" r="11" fill="#1F2937" />
  <text id="asdcaeff4-badge-2-text" x="470" y="124.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
  <rect id="asdcaeff4-rect-addon" x="1096" y="100" width="248" height="110" rx="10" fill="#FFFFFF" stroke="#0F766E" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="asdcaeff4-title-addon" x="1112" y="129" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">Add-on resources</text>
  <text id="asdcaeff4-line0-addon" x="1112" y="152" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">certificates, cluster secrets</text>
  <text id="asdcaeff4-line1-addon" x="1112" y="168" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">in agent mode, ManifestWorks</text>
  <text id="asdcaeff4-line2-addon" x="1112" y="184" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">per managed cluster</text>
  <rect id="asdcaeff4-rect-appset" x="60" y="240" width="180" height="100" rx="10" fill="#E6F4F1" stroke="#0F766E" stroke-width="1.6" />
  <text id="asdcaeff4-title-appset" x="76" y="269" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#0F766E" text-anchor="start">ApplicationSet</text>
  <text id="asdcaeff4-line0-appset" x="76" y="292" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">destination.name:</text>
  <text id="asdcaeff4-line1-appset" x="76" y="308" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">&#x27;{{name}}&#x27;</text>
  <circle id="asdcaeff4-badge-5" cx="220" cy="260" r="11" fill="#1F2937" />
  <text id="asdcaeff4-badge-5-text" x="220" y="264.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>
  <rect id="asdcaeff4-rect-applications" x="260" y="240" width="230" height="100" rx="10" fill="#FFFFFF" stroke="#C2410C" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="asdcaeff4-title-applications" x="276" y="269" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#C2410C" text-anchor="start">Applications</text>
  <text id="asdcaeff4-line0-applications" x="276" y="292" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">on the hub, one per cluster,</text>
  <text id="asdcaeff4-line1-applications" x="276" y="308" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">routed by destination.name</text>
  <rect id="asdcaeff4-rect-principal" x="520" y="240" width="250" height="100" rx="10" fill="#FDF0E8" stroke="#C2410C" stroke-width="1.6" />
  <text id="asdcaeff4-title-principal" x="536" y="269" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#C2410C" text-anchor="start">Argo CD principal</text>
  <text id="asdcaeff4-line0-principal" x="536" y="292" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">controller disabled</text>
  <text id="asdcaeff4-line1-principal" x="536" y="308" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">gRPC endpoint via Route</text>
  <circle id="asdcaeff4-badge-1" cx="750" cy="260" r="11" fill="#1F2937" />
  <text id="asdcaeff4-badge-1-text" x="750" y="264.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
  <rect id="asdcaeff4-rect-ui" x="800" y="240" width="240" height="100" rx="10" fill="#F1F5F9" stroke="#4B5563" stroke-width="1.4" />
  <text id="asdcaeff4-title-ui" x="816" y="269" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#4B5563" text-anchor="start">Argo CD UI on the hub</text>
  <text id="asdcaeff4-line0-ui" x="816" y="292" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">full resource tree,</text>
  <text id="asdcaeff4-line1-ui" x="816" y="308" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">live status of every cluster</text>
  <rect id="asdcaeff4-rect-agent" x="520" y="500" width="250" height="96" rx="10" fill="#FDF0E8" stroke="#C2410C" stroke-width="1.6" />
  <text id="asdcaeff4-title-agent" x="536" y="529" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#C2410C" text-anchor="start">Argo CD Agent</text>
  <text id="asdcaeff4-line0-agent" x="536" y="552" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">mode managed,</text>
  <text id="asdcaeff4-line1-agent" x="536" y="568" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">opens the connection</text>
  <circle id="asdcaeff4-badge-4" cx="750" cy="520" r="11" fill="#1F2937" />
  <text id="asdcaeff4-badge-4-text" x="750" y="524.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
  <rect id="asdcaeff4-rect-local" x="520" y="620" width="250" height="96" rx="10" fill="#FDF0E8" stroke="#C2410C" stroke-width="1.6" />
  <text id="asdcaeff4-title-local" x="536" y="649" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#C2410C" text-anchor="start">Local components</text>
  <text id="asdcaeff4-line0-local" x="536" y="672" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">application-controller,</text>
  <text id="asdcaeff4-line1-local" x="536" y="688" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">repo-server, Redis, no UI</text>
  <circle id="asdcaeff4-badge-6" cx="750" cy="640" r="11" fill="#1F2937" />
  <text id="asdcaeff4-badge-6-text" x="750" y="644.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">6</text>
  <rect id="asdcaeff4-rect-resources" x="800" y="620" width="240" height="96" rx="10" fill="#FFFFFF" stroke="#C2410C" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="asdcaeff4-title-resources" x="816" y="649" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#C2410C" text-anchor="start">Resources</text>
  <text id="asdcaeff4-line0-resources" x="816" y="672" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">applied locally, Git pulled</text>
  <text id="asdcaeff4-line1-resources" x="816" y="688" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">by the spoke</text>
  <rect id="asdcaeff4-rect-gitops-addon" x="1096" y="500" width="248" height="96" rx="10" fill="#F1F5F9" stroke="#4B5563" stroke-width="1.4" />
  <text id="asdcaeff4-title-gitops-addon" x="1112" y="529" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="15" font-weight="700" fill="#4B5563" text-anchor="start">gitops-addon</text>
  <text id="asdcaeff4-line0-gitops-addon" x="1112" y="552" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">installs OpenShift GitOps</text>
  <text id="asdcaeff4-line1-gitops-addon" x="1112" y="568" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#1F2937" text-anchor="start">and the agent</text>
  <circle id="asdcaeff4-badge-3" cx="1324" cy="520" r="11" fill="#1F2937" />
  <text id="asdcaeff4-badge-3-text" x="1324" y="524.5" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
  <text id="asdcaeff4-note-spoke1" x="60" y="524" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="700" fill="#1F2937" text-anchor="start">No Kubernetes credentials of the</text>
  <text id="asdcaeff4-note-spoke2" x="60" y="542" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">spoke on the hub. The agent opens</text>
  <text id="asdcaeff4-note-spoke3" x="60" y="560" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">the only connection (outbound).</text>
  <text id="asdcaeff4-note-spoke4" x="60" y="590" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12.5" font-weight="400" fill="#4B5563" text-anchor="start">No hub, still reconciliation.</text>
  <path id="asdcaeff4-flow-placement-gitopscluster" d="M 242 150 L 256 150" fill="none" stroke="#0F766E" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdcaeff4-arrow-0F766E)" />
  <path id="asdcaeff4-flow-gitopscluster-addon" d="M 492 150 L 1092 150" fill="none" stroke="#0F766E" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdcaeff4-arrow-0F766E)" />
  <text id="asdcaeff4-label-creates" x="792" y="141" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="600" fill="#0F766E" text-anchor="middle">creates</text>
  <path id="asdcaeff4-flow-addon-gitopsaddon" d="M 1220 212 L 1220 496" fill="none" stroke="#0F766E" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdcaeff4-arrow-0F766E)" />
  <text id="asdcaeff4-label-mw1" x="1230" y="408" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="600" fill="#0F766E" text-anchor="start">ManifestWorks,</text>
  <text id="asdcaeff4-label-mw2" x="1230" y="422" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="600" fill="#0F766E" text-anchor="start">pulled by the klusterlet</text>
  <path id="asdcaeff4-flow-gitopsaddon-agent" d="M 1094 548 L 774 548" fill="none" stroke="#4B5563" stroke-width="1.6" stroke-linejoin="round" marker-end="url(#asdcaeff4-arrow-4B5563)" />
  <text id="asdcaeff4-label-installs" x="934" y="540" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="600" fill="#4B5563" text-anchor="middle">installs, with client certificate</text>
  <path id="asdcaeff4-flow-placement-appset" d="M 150 202 L 150 236" fill="none" stroke="#0F766E" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdcaeff4-arrow-0F766E)" />
  <path id="asdcaeff4-flow-appset-applications" d="M 242 290 L 256 290" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdcaeff4-arrow-C2410C)" />
  <path id="asdcaeff4-flow-applications-principal" d="M 492 290 L 516 290" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdcaeff4-arrow-C2410C)" />
  <path id="asdcaeff4-flow-principal-ui" d="M 772 290 L 796 290" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdcaeff4-arrow-C2410C)" />
  <circle class="flow-dot" style="--d:1.60s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 575 342 L 575 496" begin="indefinite" data-begin="1.60" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <rect id="asdcaeff4-bg-conn-label" x="380" y="392" width="200" height="50" rx="0" fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0" />
  <path id="asdcaeff4-flow-principal-agent" d="M 575 342 L 575 496" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdcaeff4-arrow-C2410C)" />
  <text id="asdcaeff4-label-apps" x="567" y="404" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="700" fill="#C2410C" text-anchor="end">Applications</text>
  <path id="asdcaeff4-conn-agent-principal" d="M 645 496 L 645 344" fill="none" stroke="#4B5563" stroke-width="1.6" stroke-linejoin="round" stroke-dasharray="5 3" marker-end="url(#asdcaeff4-arrow-4B5563)" />
  <text id="asdcaeff4-label-conn1" x="567" y="424" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="600" fill="#4B5563" text-anchor="end">gRPC stream, mTLS,</text>
  <text id="asdcaeff4-label-conn2" x="567" y="438" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="600" fill="#4B5563" text-anchor="end">opened by the agent</text>
  <path id="asdcaeff4-status-agent-principal" d="M 715 496 L 715 344" fill="none" stroke="#0F766E" stroke-width="1.6" stroke-linejoin="round" stroke-dasharray="5 3" marker-end="url(#asdcaeff4-arrow-0F766E)" />
  <text id="asdcaeff4-label-status1" x="725" y="404" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="700" fill="#0F766E" text-anchor="start">full status back:</text>
  <text id="asdcaeff4-label-status2" x="725" y="418" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="11" font-weight="600" fill="#0F766E" text-anchor="start">resource tree, health, events</text>
  <path id="asdcaeff4-flow-agent-local" d="M 645 598 L 645 616" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdcaeff4-arrow-C2410C)" />
  <path id="asdcaeff4-flow-local-resources" d="M 772 668 L 796 668" fill="none" stroke="#C2410C" stroke-width="2" stroke-linejoin="round" marker-end="url(#asdcaeff4-arrow-C2410C)" />
  <rect id="asdcaeff4-legend-you" x="40" y="752" width="22" height="14" rx="3" fill="#E6F4F1" stroke="#0F766E" stroke-width="1.6" />
  <text id="asdcaeff4-legend-you-text" x="70" y="764" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="400" fill="#1F2937" text-anchor="start">created by you</text>
  <rect id="asdcaeff4-legend-acm" x="194.609375" y="752" width="22" height="14" rx="3" fill="#FFFFFF" stroke="#0F766E" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="asdcaeff4-legend-acm-text" x="224.609375" y="764" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="400" fill="#1F2937" text-anchor="start">generated by ACM</text>
  <rect id="asdcaeff4-legend-argo" x="370.5625" y="752" width="22" height="14" rx="3" fill="#FFFFFF" stroke="#C2410C" stroke-width="1.6" stroke-dasharray="6 4" />
  <text id="asdcaeff4-legend-argo-text" x="400.5625" y="764" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="400" fill="#1F2937" text-anchor="start">generated by Argo CD</text>
  <rect id="asdcaeff4-legend-platform" x="566.984375" y="752" width="22" height="14" rx="3" fill="#F1F5F9" stroke="#4B5563" stroke-width="1.6" />
  <text id="asdcaeff4-legend-platform-text" x="596.984375" y="764" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="400" fill="#1F2937" text-anchor="start">platform component</text>
  <text id="asdcaeff4-legend-tp" x="1380" y="764" font-family="Inter, 'Liberation Sans', Arial, sans-serif" font-size="12" font-weight="600" fill="#4B5563" text-anchor="end">Technology Preview in ACM 2.17</text>
<circle class="flow-dot" style="--d:0.00s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 242 150 L 256 150" begin="indefinite" data-begin="0.00" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:0.40s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 492 150 L 1092 150" begin="indefinite" data-begin="0.40" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:0.80s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 1220 212 L 1220 496" begin="indefinite" data-begin="0.80" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:1.20s" visibility="hidden" r="3.5" fill="#4B5563" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 1094 548 L 774 548" begin="indefinite" data-begin="1.20" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:0.40s" visibility="hidden" r="4.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 150 202 L 150 236" begin="indefinite" data-begin="0.40" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:0.00s" visibility="hidden" r="3.5" fill="#4B5563" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 645 496 L 645 344" begin="indefinite" data-begin="0.00" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:0.80s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 242 290 L 256 290" begin="indefinite" data-begin="0.80" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:1.20s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 492 290 L 516 290" begin="indefinite" data-begin="1.20" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:2.00s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 645 598 L 645 616" begin="indefinite" data-begin="2.00" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:2.40s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 772 668 L 796 668" begin="indefinite" data-begin="2.40" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:2.80s" visibility="hidden" r="3.5" fill="#0F766E" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 715 496 L 715 344" begin="indefinite" data-begin="2.80" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  <circle class="flow-dot" style="--d:3.20s" visibility="hidden" r="4.5" fill="#C2410C" stroke="#FFFFFF" stroke-width="1.5"><animateMotion path="M 772 290 L 796 290" begin="indefinite" data-begin="3.20" dur="1.40s" repeatCount="indefinite" calcMode="linear"/></circle>
  </svg>
</div>
  <div class="title">Argo CD Agent: the agent opens the connection, receives the Applications and sends the full status back</div>
</div>

</div>
<div class="paragraph">
<p>What happens under the hood:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>The Argo CD instance on the hub runs as <strong>principal</strong>. In the ACM configuration its application-controller is disabled, and a Route exposes the gRPC endpoint for the agents.</p>
</li>
<li>
<p>The <strong>GitOpsCluster</strong> with the add-on and the agent enabled creates the certificates, the cluster secrets in agent mode and the ManifestWorks for every cluster of the Placement.</p>
</li>
<li>
<p>The <strong>gitops-addon</strong> on the managed cluster installs OpenShift GitOps and the agent, including the client certificate of the agent.</p>
</li>
<li>
<p>The <strong>agent</strong> opens the connection to the principal: a gRPC stream with mutual TLS, initiated by the spoke.</p>
</li>
<li>
<p>The <strong>ApplicationSet</strong> generates the Applications on the hub. With destination-based mapping, the principal sends each Application to the agent whose name matches <code>spec.destination.name</code>.</p>
</li>
<li>
<p>The <strong>local components</strong> on the managed cluster pull Git and reconcile. The agent sends the full status back, so the Argo CD UI on the hub shows the resource tree, health and events of every cluster.</p>
</li>
</ol>
</div>
<div class="admonitionblock important">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="ulist">
<ul>
<li>
<p>The Agent mode of the GitOps add-on is <strong>Technology Preview</strong> in ACM 2.17. With ACM 5.0 which is planned for November 2026 this will become GA.</p>
</li>
<li>
<p>Every cluster that runs the Argo CD Agent requires an <strong>OpenShift Platform Plus</strong> subscription.</p>
</li>
</ul>
</div>
</td>
</tr>
</tbody></table>
</div>
</div>
<div class="sect2">
<h3 id="_step_1_prepare_the_hub_argo_cd">Step 1: Prepare the hub Argo CD</h3>
<div class="paragraph">
<p>First we need to prepare Argo CD. We need to modify the Operator Subscription and the Argo CD resource. This step has an important consequence: the Argo CD instance on the hub becomes an agent <strong>principal</strong>. Its own application-controller is <strong>disabled</strong>, so this instance no longer pushes to any cluster.</p>
</div>
<div class="paragraph">
<p>OpenShift GitOps 1.22 itself also supports a hybrid architecture (GA), where the application-controller stays enabled and push and agent Applications run side by side in one instance. The ACM 2.17 documentation for the add-on does not cover that combination yet.</p>
</div>
<div class="paragraph">
<p>First, add the required environment variables to the <code>Subscription</code> of the OpenShift GitOps Operator on the hub:</p>
</div>
<div class="listingblock linenums">
<div class="title">Subscription of the OpenShift GitOps Operator on the hub (excerpt)</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">spec:
  config:
    env:
      - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES <i class="conum" data-value="1"></i><b>(1)</b>
        value: openshift-gitops,local-cluster
      - name: ARGOCD_PRINCIPAL_TLS_SERVER_ALLOW_GENERATE <i class="conum" data-value="2"></i><b>(2)</b>
        value: &#34;false&#34;
      - name: ARGOCD_PRINCIPAL_REDIS_SERVER_ADDRESS <i class="conum" data-value="3"></i><b>(3)</b>
        value: openshift-gitops-redis:6379</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Namespaces in which the OpenShift GitOps Operator runs Argo CD instances as cluster-scoped instances, with permissions for the whole cluster. The ACM documentation lists the default instance in <code>openshift-gitops</code> and <code>local-cluster</code>, the cluster namespace of the hub itself.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Do not let the principal generate a temporary, self-signed TLS certificate. That option is meant for development only. The server certificate of the principal is provided instead (the GitOps add-on manages the PKI).</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>The Redis instance the principal uses. The default of the principal is <code>argocd-redis:6379</code>, which does not exist with OpenShift GitOps. The Redis service of the default instance is called <code>openshift-gitops-redis</code>.</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>Then configure the ArgoCD resource on the hub for agent mode:</p>
</div>
<div class="listingblock linenums">
<div class="title">ArgoCD resource on the hub in agent mode</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">apiVersion: argoproj.io/v1beta1
kind: ArgoCD
metadata:
  name: openshift-gitops
  namespace: openshift-gitops
spec:
  controller:
    enabled: false <i class="conum" data-value="1"></i><b>(1)</b>
  sourceNamespaces:
    - &#39;*&#39;
  argoCDAgent:
    principal:
      enabled: true <i class="conum" data-value="2"></i><b>(2)</b>
      destinationBasedMapping: true <i class="conum" data-value="3"></i><b>(3)</b>
      auth: &#34;mtls:CN=system:open-cluster-management:cluster:([^:]+):addon:gitops-addon:agent:gitops-addon-agent&#34; <i class="conum" data-value="4"></i><b>(4)</b>
      namespace:
        allowedNamespaces:
          - &#39;*&#39;
      server:
        route:
          enabled: true <i class="conum" data-value="5"></i><b>(5)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The hub instance no longer reconciles Applications itself.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Turns the instance into an agent principal.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Applications are routed to an agent by <code>spec.destination.name</code> instead of by namespace.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>The agent identity is taken from the client certificate that the add-on issues.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Exposes the principal with a Route. The agents connect to it, and the GitOpsCluster controller discovers its address from there.</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>Finally, the principal only propagates AppProjects whose <code>destinations</code> and <code>sourceNamespaces</code> match the agents. The ACM documentation therefore configures the <code>default</code> AppProject in <code>openshift-gitops</code> with matching (or wildcard) values.</p>
</div>
<div class="admonitionblock warning">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
If your hub Argo CD currently deploys anything via the push model, do not convert it. Install a second, dedicated Argo CD instance for the agent principal, or keep the agent for a separate hub. OpenShift GitOps 1.22 itself supports a <strong>hybrid architecture</strong> (push and agent Applications in one control plane, GA in 1.22), but the ACM 2.17 documentation for the add-on configures the hub instance with its controller disabled.
</td>
</tr>
</tbody></table>
</div>
</div>
<div class="sect2">
<h3 id="_step_2_the_gitopscluster_in_agent_mode">Step 2: The GitOpsCluster in agent mode</h3>
<div class="listingblock linenums">
<div class="title">GitOpsCluster with the GitOps add-on and the Argo CD Agent</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">apiVersion: apps.open-cluster-management.io/v1beta1
kind: GitOpsCluster
metadata:
  name: gitops-agent-clusters
  namespace: openshift-gitops
spec:
  argoServer:
    argoNamespace: openshift-gitops
  placementRef:
    kind: Placement
    apiVersion: cluster.open-cluster-management.io/v1beta1
    name: production-clusters
    namespace: openshift-gitops
  gitopsAddon:
    enabled: true <i class="conum" data-value="1"></i><b>(1)</b>
    argoCDAgent:
      enabled: true <i class="conum" data-value="2"></i><b>(2)</b>
      mode: managed <i class="conum" data-value="3"></i><b>(3)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Installs OpenShift GitOps on the spokes, as in Basic mode.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Additionally deploys the agent, creates the PKI and the cluster secrets in agent mode.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td><code>managed</code>: the hub is the source of truth for Applications. <code>autonomous</code>: the spoke owns its Applications and the hub only observes them. The default is <code>managed</code>.</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>The controller discovers the principal address from the Route.</p>
</div>
<div class="paragraph">
<p>You can check the discovered address with the following command:</p>
</div>
<div class="admonitionblock warning">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
It will take a while until it appears.
</td>
</tr>
</tbody></table>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">oc get gitopscluster gitops-agent-clusters -n openshift-gitops \
  -o jsonpath=&#39;{.spec.gitopsAddon.argoCDAgent.serverAddress}{&#34;\n&#34;}&#39;</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_3_permissions_on_the_managed_clusters">Step 3: Permissions on the managed clusters</h3>
<div class="paragraph">
<p>When the GitOpsCluster is created, ACM also creates the policy <code>&lt;gitopscluster-name&gt;-argocd-policy</code> on the hub. It is created once and then belongs to you, so it is the right place to add the permissions. Two service accounts on the spokes need them:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the <strong>agent</strong> itself, which reports the status and the live resources to the principal, needs read access,</p>
</li>
<li>
<p>the local <strong>application-controller</strong>, which actually applies the manifests, needs write access to everything the Applications deploy.</p>
</li>
</ul>
</div>
<div class="listingblock linenums">
<div class="title">Additional object templates in the gitops-agent-clusters-argocd-policy</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">            - complianceType: musthave
              objectDefinition:
                apiVersion: rbac.authorization.k8s.io/v1
                kind: ClusterRoleBinding
                metadata:
                  name: acm-openshift-gitops-argocd-agent-cluster-reader
                subjects:
                  - kind: ServiceAccount
                    name: acm-openshift-gitops-agent-agent <i class="conum" data-value="1"></i><b>(1)</b>
                    namespace: openshift-gitops
                roleRef:
                  apiGroup: rbac.authorization.k8s.io
                  kind: ClusterRole
                  name: view
            - complianceType: musthave
              objectDefinition:
                apiVersion: rbac.authorization.k8s.io/v1
                kind: ClusterRoleBinding
                metadata:
                  name: argo-admin
                subjects:
                  - kind: ServiceAccount
                    name: acm-openshift-gitops-argocd-application-controller <i class="conum" data-value="2"></i><b>(2)</b>
                    namespace: openshift-gitops
                roleRef:
                  apiGroup: rbac.authorization.k8s.io
                  kind: ClusterRole
                  name: cluster-admin <i class="conum" data-value="3"></i><b>(3)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The agent: read access is enough.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The local application-controller of the instance the add-on creates.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Simple and usually fine for platform configuration. For application teams, use the least-privilege approach from the pull model instead: the label <code>argocd.argoproj.io/managed-by</code> on the destination namespaces.</td>
</tr>
</tbody></table>
</div>
</div>
<div class="sect2">
<h3 id="_step_4_the_applicationset">Step 4: The ApplicationSet</h3>
<div class="paragraph">
<p>With destination-based mapping, the ApplicationSet looks almost like a push ApplicationSet. The only difference is the destination: <code>name</code> instead of <code>server</code>.</p>
</div>
<div class="admonitionblock note">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The push example already installed the Compliance Operator and the pull example the console branding. The agent example therefore uses a third folder, <code>clusters/&lt;cluster&gt;/branding-bottom</code>, so that the three models do not manage the same objects.
</td>
</tr>
</tbody></table>
</div>
<div class="listingblock linenums">
<div class="title">ApplicationSet for agent mode</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: branding-bottom-agent
  namespace: openshift-gitops
spec:
  generators:
    - clusterDecisionResource:
        configMapRef: acm-placement
        labelSelector:
          matchLabels:
            cluster.open-cluster-management.io/placement: production-clusters
        requeueAfterSeconds: 30
  template:
    metadata:
      name: &#39;branding-bottom-{{name}}&#39;
    spec:
      project: default
      source:
        repoURL: https://github.com/tjungbauer/openshift-clusterconfig-gitops.git
        targetRevision: main
        path: &#39;clusters/{{name}}/branding-bottom&#39; <i class="conum" data-value="1"></i><b>(1)</b>
      destination:
        name: &#39;{{name}}&#39; <i class="conum" data-value="2"></i><b>(2)</b>
      syncPolicy:
        automated:
          prune: true
          selfHeal: true</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody><tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>One folder per cluster: <code>clusters/sno/branding-bottom</code> and <code>clusters/bm/branding-bottom</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The principal routes the Application to the agent with this name, which is the name of the managed cluster.</td>
</tr>
</tbody></table>
</div>
</div>
<div class="sect2">
<h3 id="_step_5_verify">Step 5: Verify</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash"># hub: overall state, the conditions tell you which step failed
oc get gitopscluster gitops-agent-clusters -n openshift-gitops \
  -o jsonpath=&#39;{.status.conditions}&#39; | jq

# hub: the principal is running
oc get pods -n openshift-gitops -l app.kubernetes.io/name=openshift-gitops-agent-principal

# managed cluster: the agent is running
oc get pods -n openshift-gitops -l app.kubernetes.io/part-of=argocd-agent

# hub: Application status, synchronised by the agent
oc get applications.argoproj.io -n openshift-gitops</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_the_visibility_in_the_ui">The visibility in the UI</h3>
<div class="paragraph">
<p>The big advantage of the Argo CD Agent is the visibility in the UI. While the ApplicationSet in the hub shows a healthy sync status:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/gitopscollection/images/ep-19/agent-argocd-applicationset-branding-bottom-agent.png" alt="Agent Model ApplicationSet"/>
</div>
<div class="title">Figure 7. ApplicationSet in the hub shows a healthy sync status</div>
</div>
<div class="paragraph">
<p>The Application itself now also shows the full resource tree.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/gitopscollection/images/ep-19/agent-argocd-application-branding-bottom-bm.png" alt="Agent Model UI Visibility"/>
</div>
<div class="title">Figure 8. Agent Model UI Visibility</div>
</div>
<hr/>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_side_by_side">Side by side</h2>
<div class="sectionbody">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 18.1818%;"/>
<col style="width: 27.2727%;"/>
<col style="width: 27.2727%;"/>
<col style="width: 27.2728%;"/>
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Aspect</th>
<th class="tableblock halign-left valign-top">Push</th>
<th class="tableblock halign-left valign-top">Pull</th>
<th class="tableblock halign-left valign-top">Argo CD Agent (add-on)</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Status in ACM 2.17</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">GA, default</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">GA</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Technology Preview (GA in ACM 5.0)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Where Argo CD reconciles</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Hub</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Managed cluster (full Argo CD instance)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Managed cluster (agent with local Argo CD components)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Network direction</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Hub → spoke (cluster proxy or API)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Spoke → hub (klusterlet)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Spoke → hub (agent → principal)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Spoke credentials on the hub</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Token per cluster</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Cluster secret exists, not used for delivery</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">mTLS certificates, managed by the add-on</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Status visible on the hub</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Full (resource tree, live diff)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Aggregated report, not real-time</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Full, synchronised by the agent</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Hub down</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No reconciliation</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Reconciliation continues</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Reconciliation continues</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Footprint on the spoke</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">None</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Full OpenShift GitOps instance</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">OpenShift GitOps plus agent</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Git access needed from</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Hub only</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Every spoke</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Every spoke</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Can be mixed with push on the same hub instance</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">–</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Yes (unless the add-on is enabled)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Not in the ACM 2.17 documented setup (hub controller disabled)</p></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect1">
<h2 id="_summary">Summary</h2>
<div class="sectionbody">
<div class="paragraph">
<p>I have to admit that this article is a bit long. But I hope it gives you a good overview of the different models and how to set them up.</p>
</div>
<div class="paragraph">
<p>Always keep in mind:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The <strong>GitOpsCluster</strong> is the bridge: it turns placed managed clusters into Argo CD cluster secrets and keeps the tokens rotated.</p>
</li>
<li>
<p><strong>Push</strong> is the default and the easiest start: one Argo CD on the hub, full visibility, but the hub holds cluster-admin tokens for every spoke unless you use your own ManagedServiceAccount. With a huge fleet, usually greater than 20 clusters, it becomes unmanageable.</p>
</li>
<li>
<p><strong>Pull</strong> moves the reconciliation to the spokes: the hub ships Applications as ManifestWorks, a local Argo CD does the work. More scalable and outbound-only, at the price of an Argo CD per cluster and a delayed, aggregated status as well as a trade-off in visibility.</p>
</li>
<li>
<p>The <strong>Argo CD Agent add-on</strong> combines pull-style distribution with a full UI on the hub, and ACM takes care of the PKI. It is Technology Preview in ACM 2.17 and in the documented setup it turns the hub Argo CD into a principal without its own controller.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>In the <a href="/gitopscollection/2026-10-24-acm-policies-with-gitops/">last part</a>, we turn the integration around: Argo CD deploys ACM policies.</p>
</div>
<div class="paragraph">
<p>And as always: <strong>If it is not in Git, it does not exist.</strong></p>
</div>
<div class="admonitionblock note">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The diagrams in this article have been manually drawn on a whiteboard. AI then generated the SVGs from photos and made them look equal in all articles.
</td>
</tr>
</tbody></table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_references">References</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p><a href="https://docs.redhat.com/en/documentation/red_hat_advanced_cluster_management_for_kubernetes/2.17/html/gitops/gitops-overview" rel="noopener" target="_blank">ACM 2.17: GitOps (registering clusters, push and pull model, GitOps add-on, Argo CD Agent)</a></p>
</li>
<li>
<p><a href="https://docs.redhat.com/en/documentation/red_hat_openshift_gitops/1.22/html/release_notes/gitops-release-notes" rel="noopener" target="_blank">Red Hat OpenShift GitOps 1.22 release notes</a></p>
</li>
<li>
<p><a href="https://www.redhat.com/en/blog/manage-clusters-and-applications-scale-argo-cd-agent-red-hat-openshift-gitops" rel="noopener" target="_blank">Red Hat blog: Manage clusters and applications at scale with the Argo CD Agent</a></p>
</li>
<li>
<p><a href="/gitopscollection/2026-01-14-argocd-agent/">[Ep.15] OpenShift GitOps - Argo CD Agent</a></p>
</li>
</ul>
</div>
</div>
</div>]]></content:encoded></item></channel></rss>