<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Octopus blog</title>
  <subtitle>Site description.</subtitle>
  <link href="https://octopus.com/blog/feed.xml" rel="self" />
  <link href="https://octopus.com" />
  <id>https://octopus.com/blog/feed.xml</id>
  <updated>2026-06-25T00:00:00.000Z</updated>

    <entry>
      <title>Fix unsecured Argo CD communications</title>
      <link href="https://octopus.com/blog/mtls-support-argocd" />
      <id>https://octopus.com/blog/mtls-support-argocd</id>
      <published>2026-06-25T00:00:00.000Z</published>
      <updated>2026-06-25T00:00:00.000Z</updated>
      <summary>Get a deep dive of the new mTLS support in Argo CD 3.5 and explore how it enhances the security of internal communication.</summary>
      <author>
        <name>Patroklos Papapetrou, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>Securing communication between internal components is a long-awaited feature by the ArgoCD community.
Historically, to ensure encrypted communication between the <code>repo-server</code> and its internal clients, such as <code>argocd-server</code> and <code>argocd-application-controller</code>,
required operators to find solutions outside ArgoCD.</p>
<p>Argo CD 3.5 introduces native, first-class mutual TLS (mTLS) support. By embedding encryption and identity verification directly into its components,
it eliminates the need for running service mesh sidecars, writing custom certificate-rotation scripts, or managing complex volume projections
just to make sure traffic is secure between the ArgoCD components.</p>
<h2 id="why-mtls-is-one-step-ahead-of-one-way-tls">Why mTLS is one step ahead of one-way TLS</h2>
<p>Standard TLS provides one-way authentication: the client verifies the identity of the server via a certificate,
but the server accepts connections from any client inside the network boundary. So far so good!</p>
<p>Let’s take, however, the case of zero-trust architecture, where relying entirely on the network is an antipattern.
If an attacker compromises a single pod within the cluster, they can potentially communicate with the <code>repo-server</code> without any authentication.</p>
<p>Mutual TLS (mTLS) addresses this by requiring both sides to authenticate before exchanging any data:</p>
<ul>
<li>The client verifies the <code>repo-server</code> certificate to ensure it is communicating with the legitimate repository management layer.</li>
<li>The <code>repo-server</code> validates the client’s certificate against a trusted Certificate Authority (CA) to confirm that the incoming connection originates from an authorized Argo CD component.</li>
</ul>
<figure><p><img src="/blog/i/x/mtls-support-argocd/blog_1.png" alt="mTLS vs TLS" srcset="/blog/i/600/mtls-support-argocd/blog_1.webp 600w, /blog/i/1000/mtls-support-argocd/blog_1.webp 1000w, /blog/i/2000/mtls-support-argocd/blog_1.webp 1504w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="1504" height="688"></p></figure>
<p>What do you gain by using mTLS:</p>
<ul>
<li>First and most important, you stop assuming internal cluster traffic is safe. Every internal component that needs to “talk” to the <code>repo-server</code> must explicitly prove its identity.</li>
<li>If your environment is regulated by compliance frameworks such as SOC 2, HIPAA, and PCI-DSS, then you are already covered. Native mTLS satisfies the security requirements without adding third-party dependencies.</li>
<li>By issuing different certificates to different components, you can precisely log which service initiated a connection and establish a foundation for fine-grained access controls.</li>
<li>Even if an attacker gains access to a pod, they can’t actually talk to the <code>repo-server</code> because they lack the required signed client certificate.</li>
</ul>
<h2 id="the-pre-35-reality-how-operators-managed-internal-encryption">The pre-3.5 reality: how operators managed internal encryption</h2>
<p>Before native support was introduced in version 3.5, achieving mTLS within an Argo CD deployment forced teams to choose between several complex architectural workarounds.</p>
<h3 id="the-service-mesh-approach">The service mesh approach</h3>
<p>The most common pattern was transferring the encryption responsibility to an external service mesh like Istio or Linkerd.
Teams would inject sidecar proxies into their Argo CD pods to intercept traffic and handle the TLS handshake transparently.
This architecture was ok-ish. It worked, but if you ask operators, they would probably complain because they had to manage, upgrade, and debug an entirely separate control plane.
On the other side, if secure communications is a hard-requirement, then this approach was a great implementation.</p>
<figure><p><img src="/blog/i/x/mtls-support-argocd/blog_4.png" alt="Service Mesh Approach for mTLS in Argo CD" loading="lazy" srcset="/blog/i/600/mtls-support-argocd/blog_4.webp 600w, /blog/i/1000/mtls-support-argocd/blog_4.webp 1000w, /blog/i/2000/mtls-support-argocd/blog_4.webp 2000w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="3026" height="1408"></p></figure>
<h3 id="manual-cert-manager-integration">Manual cert-manager integration</h3>
<p>Other teams used <code>cert-manager</code> to automatically provision certificates into Kubernetes Secrets,
paired with custom Kustomize or Helm patches to manually map those secrets to specific paths inside the Argo deployments.
The main point of failure here was certificate rotation. Coordinating pod restarts to ensure components picked up renewed certificates often required writing custom wrapper scripts or relying on auxiliary operators like Reloader, which added more moving pieces to the platform.</p>
<h3 id="static-secret-vaulting">Static secret vaulting</h3>
<p>In highly locked-down environments, teams synchronized certificates from external stores like HashiCorp Vault using tools like the External Secrets Operator (ESO).
While this approach is very auditable-friendly, it creates a tight coupling between the secret store, the sync operator, and the GitOps controller.</p>
<p>In smaller companies, the fallback was often base64-encoding static, long-lived certificates directly into Kubernetes ConfigMaps or Secrets.
This bypassed infrastructure overhead but required manual secret rotation which was often a manual process.</p>
<p>Overall, you can see the same pattern in all three approaches described above (there might be more, but they all follow the same basic principles)
Internal communication security was handled as an external network problem rather than a native application capability, resulting in fragile configurations challenging to maintain.</p>
<h2 id="whats-new-in-35-built-in-mtls">What’s new in 3.5: built-in mTLS</h2>
<p>Argo CD 3.5 simplifies this landscape by moving the handshake, verification, and configuration logic entirely into the application.
You no longer need to write complex volume mounts or alter network layers; It’s as simple as provisioning a single, specifically named Kubernetes Secret to activate mTLS.</p>
<p>Below you can find a high-level overview of the new features:</p>
<ol>
<li>Super-easy setup (auto-discovery). Argo CD automatically looks for a Secret named <code>argocd-repo-server-mtls</code>. When discovered, the manifests handle the internal mounting and environment configurations out of the box, eliminating manual template patching.</li>
<li>The <code>repo-server</code> requires self-directed communication to execute liveness and readiness probes. In a strict mTLS environment, a service can easily block its own health checks. Argo CD 3.5 addresses this by automatically generating memory-lived, ephemeral certificates dedicated exclusively to internal loopback probes, ensuring monitoring remains functional without manual intervention.</li>
<li>The feature accommodates both simple and highly complex environments. You can start with a single shared client certificate across all components and transition to unique per-component identities later without re-architecting your underlying deployment strategy.</li>
</ol>
<figure><p><img src="/blog/i/x/mtls-support-argocd/blog_2.png" alt="mTLS handshake. How Argo CD 3.5 handles authenticates connections between Argo CD components" loading="lazy" srcset="/blog/i/600/mtls-support-argocd/blog_2.webp 600w, /blog/i/1000/mtls-support-argocd/blog_2.webp 1000w, /blog/i/2000/mtls-support-argocd/blog_2.webp 1392w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="1392" height="752"></p></figure>
<p>Now let’s take a more detailed look at the setup process for the shared-certificate configuration.</p>
<h2 id="getting-started-the-shared-certificate-setup">Getting started: the shared-certificate setup</h2>
<p>For the majority of production deployments, a single shared client certificate used by all client components is enough and requires minimum configuration</p>
<h3 id="step-1-generate-the-certificates">Step 1: Generate the certificates</h3>
<p>If you are not using an automated PKI pipeline, you can generate the required CA, server, and client certificates using standard OpenSSL commands:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">openssl</span><span style="color:#A31515"> genrsa</span><span style="color:#0000FF"> -out</span><span style="color:#A31515"> ca.key</span><span style="color:#098658"> 2048</span></span>
<span class="line"><span style="color:#795E26">openssl</span><span style="color:#A31515"> req</span><span style="color:#0000FF"> -new</span><span style="color:#0000FF"> -x509</span><span style="color:#0000FF"> -days</span><span style="color:#098658"> 365</span><span style="color:#0000FF"> -key</span><span style="color:#A31515"> ca.key</span><span style="color:#0000FF"> -out</span><span style="color:#A31515"> ca.crt</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  -subj</span><span style="color:#A31515"> "/CN=argocd-internal-repo-ca"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#795E26">openssl</span><span style="color:#A31515"> genrsa</span><span style="color:#0000FF"> -out</span><span style="color:#A31515"> server.key</span><span style="color:#098658"> 2048</span></span>
<span class="line"><span style="color:#795E26">openssl</span><span style="color:#A31515"> req</span><span style="color:#0000FF"> -new</span><span style="color:#0000FF"> -key</span><span style="color:#A31515"> server.key</span><span style="color:#0000FF"> -out</span><span style="color:#A31515"> server.csr</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  -subj</span><span style="color:#A31515"> "/CN=argocd-repo-server"</span></span>
<span class="line"><span style="color:#795E26">openssl</span><span style="color:#A31515"> x509</span><span style="color:#0000FF"> -req</span><span style="color:#0000FF"> -days</span><span style="color:#098658"> 365</span><span style="color:#0000FF"> -in</span><span style="color:#A31515"> server.csr</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  -CA</span><span style="color:#A31515"> ca.crt</span><span style="color:#0000FF"> -CAkey</span><span style="color:#A31515"> ca.key</span><span style="color:#0000FF"> -CAcreateserial</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  -out</span><span style="color:#A31515"> server.crt</span></span>
<span class="line"></span>
<span class="line"><span style="color:#795E26">openssl</span><span style="color:#A31515"> genrsa</span><span style="color:#0000FF"> -out</span><span style="color:#A31515"> client.key</span><span style="color:#098658"> 2048</span></span>
<span class="line"><span style="color:#795E26">openssl</span><span style="color:#A31515"> req</span><span style="color:#0000FF"> -new</span><span style="color:#0000FF"> -key</span><span style="color:#A31515"> client.key</span><span style="color:#0000FF"> -out</span><span style="color:#A31515"> client.csr</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  -subj</span><span style="color:#A31515"> "/CN=argocd-core-clients"</span></span>
<span class="line"><span style="color:#795E26">openssl</span><span style="color:#A31515"> x509</span><span style="color:#0000FF"> -req</span><span style="color:#0000FF"> -days</span><span style="color:#098658"> 365</span><span style="color:#0000FF"> -in</span><span style="color:#A31515"> client.csr</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  -CA</span><span style="color:#A31515"> ca.crt</span><span style="color:#0000FF"> -CAkey</span><span style="color:#A31515"> ca.key</span><span style="color:#0000FF"> -CAcreateserial</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  -out</span><span style="color:#A31515"> client.crt</span></span></code></pre>
<h3 id="step-2-construct-the-mtls-kubernetes-secret">Step 2: Construct the mTLS kubernetes secret</h3>
<p>Create a secret named <code>argocd-repo-server-mtls</code> in your Argo CD namespace. The keys within the data block must strictly conform to the expected naming convention:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#800000">apiVersion</span><span style="color:#000000">: </span><span style="color:#0000FF">v1</span></span>
<span class="line"><span style="color:#800000">kind</span><span style="color:#000000">: </span><span style="color:#0000FF">Secret</span></span>
<span class="line"><span style="color:#800000">metadata</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  name</span><span style="color:#000000">: </span><span style="color:#0000FF">argocd-repo-server-mtls</span></span>
<span class="line"><span style="color:#800000">  namespace</span><span style="color:#000000">: </span><span style="color:#0000FF">argocd</span></span>
<span class="line"><span style="color:#800000">type</span><span style="color:#000000">: </span><span style="color:#0000FF">Opaque</span></span>
<span class="line"><span style="color:#800000">data</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  client-ca.crt</span><span style="color:#000000">: </span><span style="color:#0000FF">&#x3C;BASE64_ENCODED_CA_CRT></span></span>
<span class="line"><span style="color:#800000">  client.crt</span><span style="color:#000000">: </span><span style="color:#0000FF">&#x3C;BASE64_ENCODED_CLIENT_CRT></span></span>
<span class="line"><span style="color:#800000">  client.key</span><span style="color:#000000">: </span><span style="color:#0000FF">&#x3C;BASE64_ENCODED_CLIENT_KEY></span></span>
<span class="line"><span style="color:#800000">  server-ca.crt</span><span style="color:#000000">: </span><span style="color:#0000FF">&#x3C;BASE64_ENCODED_CA_CRT></span></span></code></pre>
<p>If you are a CLI fan, you can generate and inject this secret directly using kubectl:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">kubectl</span><span style="color:#A31515"> create</span><span style="color:#A31515"> secret</span><span style="color:#A31515"> generic</span><span style="color:#A31515"> argocd-repo-server-mtls</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --from-file=client-ca.crt=ca.crt</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --from-file=client.crt=client.crt</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --from-file=client.key=client.key</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --from-file=server-ca.crt=ca.crt</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  -n</span><span style="color:#A31515"> argocd</span></span></code></pre>
<h3 id="step-3-trigger-a-rolling-restart">Step 3: Trigger a rolling restart</h3>
<p>As usual, to let the services pick up the new configuration, secrets, and certificates and initialize mTLS, you need to run a rolling restart across your deployments:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">kubectl</span><span style="color:#A31515"> rollout</span><span style="color:#A31515"> restart</span><span style="color:#0000FF"> -n</span><span style="color:#A31515"> argocd</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#A31515">  deployment/argocd-server</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#A31515">  deployment/argocd-repo-server</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#A31515">  deployment/argocd-application-controller</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#A31515">  deployment/argocd-applicationset-controller</span></span></code></pre>
<p>If you are running the application controller in a High Availability (HA) configuration, remember to target the statefulset instead:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">kubectl</span><span style="color:#A31515"> rollout</span><span style="color:#A31515"> restart</span><span style="color:#0000FF"> -n</span><span style="color:#A31515"> argocd</span><span style="color:#A31515"> statefulset/argocd-application-controller</span></span></code></pre>
<h3 id="validating-the-connection-handshake">Validating the connection handshake</h3>
<p>Once the pods are restarted, check the <code>argocd-repo-server</code> logs to confirm successful initialization.
You should observe log messages related to the self-generation of certificates for the internal health check as below:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Generated ephemeral health-check client certificate (CN=argocd-repo-server-health)</span></span></code></pre>
<p>To verify that mTLS is now enabled, try to execute a direct gRPC or HTTP request to the repo-server from an unauthenticated
pod inside the cluster. The connection should terminate immediately during the TLS handshake phase, log an untrusted client error on the server side,
and prevent any data exposure.</p>
<p>To the contrary all internal communications to <code>repo-server</code> are now encrypted.</p>
<p>So far we have seen how easy to enable mTLS using a default approach, same client certificate across all components. This probably works for most of the production environments,
but there are cases where you might want to use different certificates for different components. The following sections will show you how to do that.</p>
<h2 id="advanced-configurations-per-component-certificates">Advanced configurations: per-component certificates</h2>
<p>In enterprise environments with strict auditing requirements or multi-tenant architectures, sharing a single client certificate across all services may violate compliance rules.
Argo CD 3.5 supports unique and different, per-component certificates through the following two approaches.</p>
<figure><p><img src="/blog/i/x/mtls-support-argocd/blog_3.png" alt="Advanced Configurations: Per-Component Identities" loading="lazy" srcset="/blog/i/600/mtls-support-argocd/blog_3.webp 600w, /blog/i/1000/mtls-support-argocd/blog_3.webp 1000w, /blog/i/2000/mtls-support-argocd/blog_3.webp 1392w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="1392" height="752"></p></figure>
<h3 id="option-a-multiple-keys-within-a-single-secret">Option A: multiple keys within a single secret</h3>
<p>You can store all individual component certificates inside the primary <code>argocd-repo-server-mtls</code> secret using distinct key identifiers.
This centralizes your secret management while allowing smooth delivery to each client component via volume projection patches.
Check the sample <code>yaml</code> below. You can see that for each component we have a separate key for the client certificate (<code>server-*</code>, <code>controller-*</code>, etc.)</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#800000">apiVersion</span><span style="color:#000000">: </span><span style="color:#0000FF">v1</span></span>
<span class="line"><span style="color:#800000">kind</span><span style="color:#000000">: </span><span style="color:#0000FF">Secret</span></span>
<span class="line"><span style="color:#800000">metadata</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  name</span><span style="color:#000000">: </span><span style="color:#0000FF">argocd-repo-server-mtls</span></span>
<span class="line"><span style="color:#800000">  namespace</span><span style="color:#000000">: </span><span style="color:#0000FF">argocd</span></span>
<span class="line"><span style="color:#800000">type</span><span style="color:#000000">: </span><span style="color:#0000FF">Opaque</span></span>
<span class="line"><span style="color:#800000">data</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  client-ca.crt</span><span style="color:#000000">: </span><span style="color:#0000FF">&#x3C;BASE64_CA_PEM></span></span>
<span class="line"><span style="color:#800000">  server-client.crt</span><span style="color:#000000">: </span><span style="color:#0000FF">&#x3C;BASE64_SERVER_CERT_PEM></span></span>
<span class="line"><span style="color:#800000">  server-client.key</span><span style="color:#000000">: </span><span style="color:#0000FF">&#x3C;BASE64_SERVER_KEY_PEM></span></span>
<span class="line"><span style="color:#800000">  controller-client.crt</span><span style="color:#000000">: </span><span style="color:#0000FF">&#x3C;BASE64_CONTROLLER_CERT_PEM></span></span>
<span class="line"><span style="color:#800000">  controller-client.key</span><span style="color:#000000">: </span><span style="color:#0000FF">&#x3C;BASE64_CONTROLLER_KEY_PEM></span></span></code></pre>
<p>Once you have added the above secret to K8s, patch the volume mount configuration of each component deployment,
ensuring it maps its custom key to the filename the client expects (client.crt):</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#800000">spec</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  template</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">    spec</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">      volumes</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#000000">      - </span><span style="color:#800000">name</span><span style="color:#000000">: </span><span style="color:#0000FF">argocd-repo-server-mtls</span></span>
<span class="line"><span style="color:#800000">        secret</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">          secretName</span><span style="color:#000000">: </span><span style="color:#0000FF">argocd-repo-server-mtls</span></span>
<span class="line"><span style="color:#800000">          items</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#000000">          - </span><span style="color:#800000">key</span><span style="color:#000000">: </span><span style="color:#0000FF">server-client.crt</span></span>
<span class="line"><span style="color:#800000">            path</span><span style="color:#000000">: </span><span style="color:#0000FF">client.crt</span></span>
<span class="line"><span style="color:#000000">          - </span><span style="color:#800000">key</span><span style="color:#000000">: </span><span style="color:#0000FF">server-client.key</span></span>
<span class="line"><span style="color:#800000">            path</span><span style="color:#000000">: </span><span style="color:#0000FF">client.key</span></span>
<span class="line"><span style="color:#000000">          - </span><span style="color:#800000">key</span><span style="color:#000000">: </span><span style="color:#0000FF">client-ca.crt</span></span>
<span class="line"><span style="color:#800000">            path</span><span style="color:#000000">: </span><span style="color:#0000FF">client-ca.crt</span></span></code></pre>
<p>The main advantage of this approach is that you are still keeping all certificates in a single secret.
Obviously the disadvantage is that you need to maintain explicit volume mount overrides across all components that need to talk to <code>repo-server</code>.</p>
<h3 id="option-b-isolated-secret-objects-per-component">Option B: isolated secret objects per component</h3>
<p>The second approach suggests breaking the secret configuration to individual, component-specific secrets (e.g., <code>argocd-repo-server-mtls-server</code>, <code>argocd-repo-server-mtls-controller</code>):
So for each component that communicates with <code>repo-server</code>, you create a separate secret with the appropriate client certificate and key. For example:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#800000">apiVersion</span><span style="color:#000000">: </span><span style="color:#0000FF">v1</span></span>
<span class="line"><span style="color:#800000">kind</span><span style="color:#000000">: </span><span style="color:#0000FF">Secret</span></span>
<span class="line"><span style="color:#800000">metadata</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  name</span><span style="color:#000000">: </span><span style="color:#0000FF">argocd-repo-server-mtls-server</span></span>
<span class="line"><span style="color:#800000">  namespace</span><span style="color:#000000">: </span><span style="color:#0000FF">argocd</span></span>
<span class="line"><span style="color:#800000">type</span><span style="color:#000000">: </span><span style="color:#0000FF">Opaque</span></span>
<span class="line"><span style="color:#800000">data</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  client.crt</span><span style="color:#000000">: </span><span style="color:#0000FF">&#x3C;BASE64_SERVER_CERT_PEM></span></span>
<span class="line"><span style="color:#800000">  client.key</span><span style="color:#000000">: </span><span style="color:#0000FF">&#x3C;BASE64_SERVER_KEY_PEM></span></span></code></pre>
<p>Then you need to patch again each deployment’s volume specification to point to its corresponding secret:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#800000">spec</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  template</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">    spec</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">      volumes</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#000000">      - </span><span style="color:#800000">name</span><span style="color:#000000">: </span><span style="color:#0000FF">argocd-repo-server-mtls</span></span>
<span class="line"><span style="color:#800000">        secret</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">          secretName</span><span style="color:#000000">: </span><span style="color:#0000FF">argocd-repo-server-mtls-server</span></span></code></pre>
<p>This is the most clear approach because you keep the secrets for each component isolated from the others.
This allows you to rotate keys independently of the primary secret, which is a common practice in multi-tenant environments.</p>
<p>But as you know, there’s no free beer. The downside (if you think of it as a downside) of this approach is that you need to maintain separate Kubernetes Secret objects for each component.</p>
<h2 id="real-world-use-cases">Real-world use cases</h2>
<h3 id="comprehensive-zero-trust-layering">Comprehensive zero-trust layering</h3>
<p>Native mTLS provides a critical mid-tier authentication layer that complements existing security layers.
A strong and complete defense-in-depth model for Argo CD typically relies on the following layers:</p>
<ol>
<li>Network policies that restrict pod-to-pod communication paths so that only valid Argo CD components can route packets to the <code>repo-server</code> port.</li>
<li>K8s service accounts that define exactly what an authenticated client process is allowed to execute once the connection is opened.</li>
<li>Native mTLS which is used to authenticate data exchange between services at the application layer.</li>
<li>Application RBAC which allows fine-grained access control to Argo CD resources.</li>
</ol>
<h3 id="audit-ready-compliance-soc-2--hipaa--pci-dss">Audit-ready compliance (SOC 2 / HIPAA / PCI-DSS)</h3>
<p>When preparing for an audit, proving internal data security can be challenging when relying on third-party service meshes or complex bespoke scripts.
Auditors look for easily verifiable, reproducible security controls. Pointing to a native, platform-supported configuration driven by a declarative Kubernetes Secret simplifies the compliance narrative considerably compared to explaining a custom infrastructure mesh setup.</p>
<h3 id="multi-tenant-control-planes">Multi-tenant control planes</h3>
<p>For large organizations running multi-tenant internal developer platforms (IDPs), combining per-component certificates with upstream gRPC interceptors allows cluster administrators to log, trace, and isolate internal requests precisely by team or business unit.
This level of traceability is highly beneficial for forensics and chargeback metrics.</p>
<h2 id="migration-patterns">Migration patterns</h2>
<p>Transitioning an active production cluster from a service mesh or custom cert-manager architecture to native mTLS might sound like a complex task but
it can be executed with minimal risk using the following, incremental, approach:</p>
<ol>
<li>You can keep your existing Root CA. Use your current certificate authority to generate the initial client certificates and keys.</li>
<li>Deploy the <code>argocd-repo-server-mtls</code> secret into the cluster while your existing sidecars or custom mounts are active. The presence of the secret will not change anything until a reload occurs.</li>
<li>Execute a rolling restart of all Argo CD components to pick up the new configuration.</li>
<li>Verify that the connection handshake (via the native mTLS configuration) is successful and that all internal traffic is now encrypted.</li>
<li>Once the connection is established, remove the old configuration, disable sidecar injection, etc.</li>
</ol>
<p>Well done! – you are now using native mTLS for all internal traffic.</p>
<h2 id="architectural-recommendation">Architectural recommendation</h2>
<p>This article cannot cover all the production cases out there but we can summarize the recommendations for two groups of implementations.
If you are operating a small-to-medium-sized Argo CD installation looking for an immediate security upgrade with low operational friction and no specific requirement to audit individual internal component traffic streams.
On the other side, if you are looking for a more robust and comprehensive security model, because, for instance, you are in a highly regulated enterprise environment,
then you should consider the per-component approach that gives you the most flexibility and control.</p>
<p>For most teams, starting with a shared certificate configuration is the most pragmatic approach.
The good (excellent) news is that if your compliance needs scale over time, migrating to separate component identities involves adjusting your manifest overlays without replacing your underlying secrets architecture.</p>]]></content>
    </entry>
    <entry>
      <title>The Bullseye Model of product velocity</title>
      <link href="https://octopus.com/blog/the-bullseye-model-for-product-velocity" />
      <id>https://octopus.com/blog/the-bullseye-model-for-product-velocity</id>
      <published>2026-06-25T00:00:00.000Z</published>
      <updated>2026-06-25T00:00:00.000Z</updated>
      <summary>Discover a model that encourages product development teams to go beyond throughput to what really matters.</summary>
      <author>
        <name>Paul Stovell, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>One of the greatest challenges to traditional software delivery thinking was the revelation that you can deliver software quickly without causing instability. The speed-versus-stability trade-off was so widely accepted that even a decade after the myth was busted, many people still believe it.</p>
<p>For those who have accepted that you can move fast without breaking things, a second myth is emerging: The myth of speed.</p>
<p>When you look at teams that deploy frequently with short lead times, you’ll find they are generally more successful than those who work in larger batches. This checks out when you consider the additional work large batches cause. There’s also a benefit to the automation you introduce to achieve higher throughput.</p>
<p>But if you think speed alone is driving the success of these teams, their products, or the organization’s market share, you’re making a common mistake.</p>
<p>You can develop a better understanding of the broader factors that contribute to success using the Bullseye Model, which captures the nuance of product velocity.</p>
<h2 id="a-quick-archery-lesson">A quick archery lesson</h2>
<p>Many sports present a target with a bullseye, but we’ll use archery as an example today.</p>
<p>When you practice shooting a bow, your goal isn’t to hit the bullseye (though that will become the goal later). Instead, you should develop consistency so your arrows land in a tight group. Until you can consistently group your arrows, any bullseye you hit is chance, not skill.</p>
<p>Much of the skill in archery comes from the discipline of form. You have to develop consistency in where you place your feet, how you hold your body, the way you draw the bow, and how smoothly you release the string. You repeat this by making very deliberate moves many times over, working through a checklist in your mind each time, until it becomes automatic.</p>
<figure><p><img src="/blog/i/x/the-bullseye-model-for-product-velocity/grouping.png" alt="Two targets, one with holes scattered across the surface and one with a tight grouping of holes in the top left" loading="lazy" srcset="/blog/i/600/the-bullseye-model-for-product-velocity/grouping.webp 600w, /blog/i/1000/the-bullseye-model-for-product-velocity/grouping.webp 1000w, /blog/i/2000/the-bullseye-model-for-product-velocity/grouping.webp 2000w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="4096" height="2080"></p><figcaption>While the left target scores more, the right target shows skill and consistency.</figcaption></figure>
<p>Once you’ve developed these skills, missing the bullseye becomes part of the feedback loop. When you know you’ve got all the movements right, you’re able to attribute any drift to other sources. You can now adjust your aim to compensate for the many variables, like wind speed, moisture in the air, and distance, and you develop a feeling for when you’ve damaged an arrow and need to repair it.</p>
<p>If you were betting on an archer based on scores, you’d likely pick the left-hand target. You now know that, over the course of a whole competition, the right-hand target was made by the archer more likely to win. When an archer can group their arrows in a tight bunch, they can move that group closer to the bullseye and achieve far higher scores than those hitting the target randomly.</p>
<h2 id="why-speed-isnt-enough">Why speed isn’t enough</h2>
<p>Let’s return to the topic of building software. We need to establish why speed alone isn’t a sufficient strategy. If you’ve been following the research, you’ll be familiar with the insight that deploying more often with short lead times makes your team, software product, and organization more successful (source: <a href="https://dora.dev/research/">DORA</a>). Interpreting this as “speed equals success” is a simplification of the true picture.</p>
<p>Many teams and organizations are working hard to improve their software delivery performance without realizing a crucial element is missing from their approach. Alongside the smooth flow of changes, you need to develop a deep understanding of the user, their goals, and what’s causing them problems and friction.</p>
<p>The research captures this in a construct called “<a href="https://dora.dev/capabilities/user-centric-focus/">user-centric focus</a>,” and the Bullseye Model is how you navigate it.</p>
<figure><p><img src="/blog/i/x/the-bullseye-model-for-product-velocity/target.png" alt="An archery target with concentric circles of white, black, blue, red, and a golden bullseye in the center." loading="lazy" srcset="/blog/i/600/the-bullseye-model-for-product-velocity/target.webp 600w, /blog/i/1000/the-bullseye-model-for-product-velocity/target.webp 1000w, /blog/i/2000/the-bullseye-model-for-product-velocity/target.webp 2000w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="2048" height="2080"></p><figcaption>An archery target with the perfect product represented by the bullseye.</figcaption></figure>
<p>The bullseye sits at the center of the target. It represents an absolutely perfect product-market fit for your software product. If you imagine the best product your customers could ever want, that’s the bullseye. Except you can’t imagine it, which is why you need to apply the Bullseye Model.</p>
<h2 id="product-velocity">Product velocity</h2>
<p>There are infinite paths for developing a software system. The two most interesting are where you are now compared to the ideal product you could build. The concept of the ideal product is asymptotic, but it’s better, faster, safer, and cheaper than your competitor’s products.</p>
<p>To map product velocity, you need to know your starting point. What you’ve built likely isn’t the absolute best product, so your customers’ needs are not fully met. Your starting point will be somewhere other than the bullseye. It may be close to the gold circle, or it could be wallowing in the very edge of the last white one.</p>
<p>From this starting point, you create a roadmap, which is your hypothesis about what will make your software look more like the ideal product. Each roadmap entry is another arrow hitting the target, and each time you shoot an arrow, you have the opportunity to observe where it landed.</p>
<p>The line between the starting point and the new arrow has a distance and direction. This is your vector, telling you whether you’re heading in the right direction and how quickly. You have to watch both, as only a careless organization celebrates speed without paying attention to direction.</p>
<figure><p><img src="/blog/i/x/the-bullseye-model-for-product-velocity/target-vector.png" alt="Two holes with an arrow showing the distance and direction between the shots" loading="lazy" srcset="/blog/i/600/the-bullseye-model-for-product-velocity/target-vector.webp 600w, /blog/i/1000/the-bullseye-model-for-product-velocity/target-vector.webp 1000w, /blog/i/2000/the-bullseye-model-for-product-velocity/target-vector.webp 2000w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="2048" height="2080"></p><figcaption>Vectors have distance and direction</figcaption></figure>
<p>The direction of the vector tells you how well your roadmap is servicing the hunt for the ideal product. If the vector points to the bullseye, your assumptions were perfect. More often, you’ll find you’re making less efficient moves.</p>
<p>When you move away from the bullseye, your hypothesis is invalid, and you need to adjust course. You might back up and try a different approach (returning to the previous starting point), or incorporate feedback that changes the outcome (continue from where your arrow landed, hoping to flip the loss to a gain).</p>
<h2 id="when-speed-does-matter">When speed does matter</h2>
<p>On the Bullseye Model, speed refers to how quickly you can prepare and shoot each arrow. We can apply the lessons from the research here, as we don’t want throughput (lots of arrows) without stability (hitting the target).</p>
<p>Speed does matter, but it’s not the goal. Your ultimate aim is to reach the bullseye, but being able to smoothly and quickly deliver arrows creates more opportunities to check your vector and adjust course. Crucially, when you start trending away from the center, you can rapidly reverse course without over-investing in the wrong approach. You might start building an “offline mode” only to discover users are confused by how you’ve implemented it, or that they don’t value the idea at all and would prefer to be notified when they are back online.</p>
<p>Any speed you have in excess of your ability to check your vector and change course is wasted, as it’s as likely to move you rapidly away from the bullseye as it is to move you closer. You need to develop feedback cycles in concert with your software delivery throughput, while maintaining stable operations.</p>
<p>Moving in the right direction is the primary concern and high-throughput is a valuable way to check your vector more often.</p>
<figure><p><img src="/blog/i/x/the-bullseye-model-for-product-velocity/vector-comparison.png" alt="An origin hole and two different next shot possibilities, one is a moderate improvement, the other moves away from the bullseye" loading="lazy" srcset="/blog/i/600/the-bullseye-model-for-product-velocity/vector-comparison.webp 600w, /blog/i/1000/the-bullseye-model-for-product-velocity/vector-comparison.webp 1000w, /blog/i/2000/the-bullseye-model-for-product-velocity/vector-comparison.webp 2000w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="2048" height="2080"></p><figcaption>Modest progress in the right direction always beats a great leap in the wrong direction</figcaption></figure>
<h2 id="ai-makes-deployment-pipelines-crucial">AI makes deployment pipelines crucial</h2>
<p>The introduction of AI-assisted software development means teams have the potential to increase their throughput. There are preconditions for this to be true: the techniques and practices described in Continuous Delivery, as evidenced by research programs like DORA.</p>
<p>When you increase change throughput, you must adapt to this rate of change in other areas, or queues will eat all your gains. Manual stages in your deployment pipeline need refinement. You may automate, augment, or prioritize them, but leaving them as they were before AI-assisted development is not an option.</p>
<p>Deployment governance has become crucial in regulated organizations and those working at scale, as fragmented governance processes will result in a total loss of all return on your AI investments. Your default path to production must be the easiest, safest, and most compliant way to deliver changes, with no exceptions or expediting.</p>
<p>The Bullseye Model also reminds you that the rate at which you can collect and absorb feedback is a crucial consideration of how quickly you can introduce changes. If you can’t tell if a change is moving you toward the ideal product, you’re simply spinning out of control.</p>
<p>While our competitors are counting token use, pull requests, or lines of code, the thing that we should focus on above all else is how rapidly we are closing the gap between where our product is today and the ideal product described in our roadmap.</p>
<p>Happy deployments!</p>]]></content>
    </entry>
    <entry>
      <title>Optional ephemeral environments using GitHub PR labels</title>
      <link href="https://octopus.com/blog/optional-ephemeral-environments-gh-label" />
      <id>https://octopus.com/blog/optional-ephemeral-environments-gh-label</id>
      <published>2026-06-23T00:00:00.000Z</published>
      <updated>2026-06-23T00:00:00.000Z</updated>
      <summary>Ephemeral environments are great for PR reviews, but they're not always needed. In this post, I walk through how I used a simple GitHub PR label to make ephemeral environment deployments optional without breaking branch protection rules.</summary>
      <author>
        <name>Matthew Allford, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>Here at Octopus Deploy, I recently modernized our build and release processes for several static sites we refer to as microsites. The new process includes deploying an <a href="https://octopus.com/blog/introducing-ephemeral-environments">ephemeral environment</a> for every Pull Request (PR) using Octopus Deploy and commenting on the PR with a link to the ephemeral environment. This provides the PR creator and any reviewers with a real, working environment to review the proposed changes.</p>
<p>There are many things I like about ephemeral environments, and I think they add significant value to the software development lifecycle. However, I have recently been asked a few times whether ephemeral environments can be skipped as part of the PR process. Once was at NDC Sydney, where I gave a talk on Ephemeral Environments, and another was by a colleague who regularly works on these microsites internally.</p>
<p>I sat down to think about this, and refactored the GitHub workflow to accommodate the request.</p>
<h2 id="why-skip-ephemeral-environments">Why skip ephemeral environments?</h2>
<p>Ephemeral environments are valuable, but as I learned through these recent conversations, they’re not always required. Our ephemeral environment process for microsites, which creates an Azure Storage account with Terraform and then deploys the website package to the target storage account, can take 5 to 9 minutes.</p>
<p>Our GitHub repositories have protections on the <code>main</code> branch, and in addition to a required review, we have checks that must pass before the PR can be merged. Some of those checks check spelling and Markdown syntax, but another checks that the build process completed successfully. Part of that build triggers the ephemeral environment deployment in Octopus Deploy, so deploying the ephemeral environment can delay the build’s completion.</p>
<p>Another reason that came up in a community discussion was the issue of busy repositories and the amount of infrastructure that may be provisioned when environments are spun up for each PR. Especially if those environments have high costs, there could be real cost implications at scale. Though I’d also argue that there are other factors to investigate, like the duration of opened PRs, to see if optimizations can be made elsewhere.</p>
<p>The common thread across all these scenarios is that the decision to deploy an ephemeral environment is contextual; it depends on the nature of the change and the needs of the people reviewing it. Rather than removing ephemeral environments from the process entirely, what I needed was a way to make them optional on a per-PR basis, with minimal friction for the developer raising the PR.</p>
<p>It turned out that GitHub’s PR labels were the perfect tool for the job.</p>
<h2 id="skipping-ephemeral-environments-with-a-pr-label">Skipping ephemeral environments with a PR label</h2>
<p>The original GitHub workflow file only had one job. All steps were within the single job, and the required checks for the GitHub repo were configured to verify a successful run of this single job before allowing the PR to be merged.</p>
<p>While you can make steps in a workflow conditional, the more I looked at it, the more I realized it made sense to break the workflow into multiple jobs to achieve different outcomes.</p>
<ul>
<li><strong><code>build</code></strong> — always runs on every PR. It installs dependencies, builds the site, runs tests, packages the artifact, and uploads it for use by subsequent jobs.</li>
<li><strong><code>deploy-ephemeral</code></strong> — picks up the artifact from the <code>build</code> job, pushes it to Octopus Deploy, creates the ephemeral environment, and deploys the release. This is the job we want to make optional.</li>
<li><strong><code>pr-ready</code></strong> — a lightweight gate job that acts as the required status check for the <code>main</code> branch. It verifies that <code>build</code> succeeded and that <code>deploy-ephemeral</code> either succeeded or was deliberately skipped.</li>
</ul>
<p>The key to making <code>deploy-ephemeral</code> optional is a single <code>if:</code> condition on the job:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#800000">deploy-ephemeral</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  runs-on</span><span style="color:#000000">: </span><span style="color:#0000FF">ubuntu-latest</span></span>
<span class="line"><span style="color:#800000">  needs</span><span style="color:#000000">: </span><span style="color:#0000FF">build</span></span>
<span class="line"><span style="color:#800000">  if</span><span style="color:#000000">: </span><span style="color:#0000FF">${{ !contains(github.event.pull_request.labels.*.name, 'skip-ephemeral') }}</span></span>
<span class="line"><span style="color:#098658">  ...</span></span></code></pre>
<p>This checks whether the PR has a label named “skip-ephemeral”. If the label is present, the entire job is skipped. If it isn’t, the job runs as normal. Adding the label to a PR before opening it is all that’s needed to skip the deployment to the ephemeral environment.</p>
<p>But by breaking the workflow into two jobs, I introduced another problem I needed to solve.</p>
<h2 id="using-a-gate-job-to-keep-branch-protection-intact">Using a gate job to keep branch protection intact</h2>
<p>Previously, the required check on the main branch pointed directly at the single build job, and if it passed, the PR could be merged. With the refactored workflow, I couldn’t configure the required check to check the status of <code>deploy-ephemeral</code> anymore, because that job might be intentionally skipped. A skipped job registers as a failed required check in GitHub, which would block the PR from merging even when everything went exactly as intended.</p>
<p>I needed a reliable way to signal whether the overall workflow succeeded or failed, regardless of whether the ephemeral deployment ran. That’s where the <code>pr-ready</code> gate job comes in.</p>
<p>The <code>pr-ready</code> job runs after both <code>build</code> and <code>deploy-ephemeral</code>, and uses <code>if: always()</code> to ensure it runs regardless of what happened upstream. Inside the job, a single step checks two conditions:</p>
<ul>
<li><code>build</code> must have succeeded, as there’s no scenario where a failed build should result in a mergeable PR</li>
<li><code>deploy-ephemeral</code> must have either succeeded or been deliberately skipped. A failure in the deployment job should still block the merge, but a skip should not</li>
</ul>
<p>If either of those conditions isn’t met, the step runs <code>exit 1</code>, which fails the job and blocks the PR from merging. The required check on <code>main</code> now checks the <code>pr-ready</code> job rather than either of the other two, so it always has a clear, predictable result to work with.</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#800000">pr-ready</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  runs-on</span><span style="color:#000000">: </span><span style="color:#0000FF">ubuntu-latest</span></span>
<span class="line"><span style="color:#800000">  needs</span><span style="color:#000000">: [</span><span style="color:#0000FF">build</span><span style="color:#000000">, </span><span style="color:#0000FF">deploy-ephemeral</span><span style="color:#000000">]</span></span>
<span class="line"><span style="color:#800000">  if</span><span style="color:#000000">: </span><span style="color:#0000FF">always()</span></span>
<span class="line"></span>
<span class="line"><span style="color:#800000">  steps</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#000000">    - </span><span style="color:#800000">name</span><span style="color:#000000">: </span><span style="color:#0000FF">Verify required checks</span></span>
<span class="line"><span style="color:#800000">      run</span><span style="color:#000000">: </span><span style="color:#0000FF">exit 1</span></span>
<span class="line"><span style="color:#800000">      if</span><span style="color:#000000">: </span><span style="color:#AF00DB">></span><span style="color:#0000FF">-</span></span>
<span class="line"><span style="color:#0000FF">        needs.build.result != 'success' ||</span></span>
<span class="line"><span style="color:#0000FF">        (needs.deploy-ephemeral.result != 'success' &#x26;&#x26;</span></span>
<span class="line"><span style="color:#0000FF">        needs.deploy-ephemeral.result != 'skipped')</span></span></code></pre>
<h2 id="making-it-simple-to-use">Making it simple to use</h2>
<p>I wanted to make it as easy as possible for developers to skip an ephemeral environment, should they want to. When creating a PR, all they need to do is add the label <code>skip-ephemeral</code> to the PR. When the PR is opened, the GitHub workflow will start, read the labels assigned to the PR, and skip the <code>deploy-ephemeral</code> job. The build still runs as expected, and the <code>pr-ready</code> job will execute, which is the job being tracked by required checks for PRs.</p>
<figure><p><img src="/blog/img/optional-ephemeral-environments-gh-label/github-pull-request-label.png" alt="Adding a label to a GitHub Pull Request"></p><figcaption>Adding the label during PR creation.</figcaption></figure>
<p>It’s worth noting that the label needs to be present before the workflow triggers. If you open a PR without the label and the workflow starts running, adding the label mid-run won’t affect that run. The label state is read from the event payload when the workflow was triggered. If you push a subsequent commit, the workflow will re-trigger and read the current label state at that point, so the skip will take effect from your next push onwards.</p>
<h2 id="one-change-every-team-benefits">One change, every team benefits</h2>
<p>One of the things I enjoy about working with a Platform Engineering mindset is that the improvements I make to shared infrastructure benefit everyone who relies on it. In this case, I was responding to a request from one of our application teams, but because our microsite build process is templated and shared across multiple teams, the change I made is immediately available to every team using the same workflow. At the same time, the platform team can also decline a feature request. If skipping ephemeral environments conflicted with the organization’s risk, compliance, or deployment policies, we could simply choose not to implement it, and every team consuming the shared workflow would remain compliant by default.</p>
<p>This is the value of treating your pipelines as shared, versioned infrastructure rather than copies of a golden template. When every team has its own copy of a pipeline, improvements stay local to the team, making it harder for the platform team to push changes out. When teams consume a shared workflow, a single change propagates everywhere, everyone benefits, and the platform team can focus on building improvements rather than coordinating rollouts.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Ephemeral environments are a powerful tool for reviewing changes in a real environment before they’re merged, but there are scenarios where developers may want to skip the ephemeral environment process. It’s not something I’d lean towards myself, and I definitely wouldn’t make that the default behavior.</p>
<p>By breaking the workflow into separate jobs and using a simple-to-use PR label as a condition, it’s possible to make the ephemeral environment deployment optional without compromising the integrity of the build process or the branch protection rules that rely on it.</p>
<p>Happy deployments!</p>]]></content>
    </entry>
    <entry>
      <title>GitHub Connections now available for self-hosted Octopus Server customers</title>
      <link href="https://octopus.com/blog/github-connections-self-hosted" />
      <id>https://octopus.com/blog/github-connections-self-hosted</id>
      <published>2026-06-18T00:00:00.000Z</published>
      <updated>2026-06-18T00:00:00.000Z</updated>
      <summary>GitHub Connections, via the Octopus Deploy App for GitHub, are now available for self-hosted Octopus Server customers</summary>
      <author>
        <name>Michael Richardson, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>As of release 2026.2, GitHub Connections (via the Octopus Deploy App for GitHub) are available to self-hosted Octopus Server customers.</p>
<p>In 2024, we <a href="https://octopus.com/blog/github-app">launched</a> the <a href="https://github.com/marketplace/octopus-deploy">Octopus Deploy App in the GitHub Marketplace</a>. It has been widely adopted, unsurprisingly, as it is the most seamless way to connect Octopus Deploy and GitHub. At launch, however, it did <em>not</em> support self-hosted Octopus Server customers.</p>
<p>This means until now, if you self-host your Octopus instance and you integrate with GitHub, you’ve been doing it with Personal Access Tokens (PATs). PATs work, but they’ve never been ideal for a critical integration like this, suffering from a number of problems:</p>
<ul>
<li>Someone leaves the team and their PAT goes with them</li>
<li>A token quietly expires at 2am and a deployment falls over</li>
<li>PATs are user-scoped, long-lived, and often have more permissions than they strictly need</li>
</ul>
<p>We’ve received plenty of feedback requesting support for the Octopus Deploy App for GitHub from our self-hosted customers, and we’re happy to say that as of release 2026.2 it’s available.</p>
<h2 id="whats-shipping">What’s shipping</h2>
<p><a href="https://octopus.com/docs/projects/version-control/github#github-app-connections">GitHub Connections</a> are now available for self-hosted Octopus Deploy. You can install the Octopus Deploy App for GitHub, connect it to your self-hosted instance, and use it anywhere the product currently requires GitHub credentials.</p>
<p>Under the hood, GitHub Connections use OpenID Connect (OIDC) to exchange a signed token from your Octopus instance for a short-lived GitHub token, scoped to the repositories you’ve granted the app access to. There are no long-lived secrets to store, rotate, or leak. The permissions are managed in GitHub against the app installation, not against a user account. When someone leaves the team, nothing breaks.</p>
<p><img src="/blog/img/github-connections-self-hosted/github-connections-page.png" alt="GitHub Connections"></p>
<h2 id="what-about-octopus-instances-behind-a-firewall">What about Octopus instances behind a firewall?</h2>
<p>This is the part that took the most thought.</p>
<p>OIDC requires GitHub to fetch the public signing keys from your Octopus instance to verify the tokens it issues. That works fine on Cloud, and it works fine on a self-hosted instance that’s reachable from the public internet. It doesn’t work if your instance lives behind a corporate firewall, on a private network, or otherwise isn’t reachable from github.com, and this is true for many self-hosted installs.</p>
<p>So we’ve added a second option. On the <strong>Signing Keys</strong> settings page, you’ll find a new <strong>Externally Hosted</strong> option. You generate your signing keys in Octopus as usual, then publish the public keys to any web host you like, such as an S3 bucket or Azure blob storage, whatever fits your infrastructure, and tell Octopus the OIDC issuer URL where GitHub can fetch them. Octopus signs tokens with that issuer URL, GitHub validates them against the published keys, and your private instance never has to take an inbound connection from the internet.</p>
<p>The <a href="https://oc.to/signing-keys">signing keys docs</a> walk through the options.</p>
<p><img src="/blog/img/github-connections-self-hosted/signing-key-config.png" alt="Signing Keys Configuration"></p>
<h2 id="getting-started">Getting started</h2>
<p>GitHub Connections are available to all self-hosted customers from <strong>2026.2</strong>. See the <a href="https://oc.to/githubapp">GitHub Connections docs</a> for the details of how to configure the integration between Octopus Deploy and GitHub, <em>without</em> Personal Access Tokens.</p>
<div class="hint"><p>Note: GitHub Connections work with repositories hosted on github.com. They don’t yet support GitHub Enterprise Server (self-hosted GitHub). If your repositories live on GitHub Enterprise Server, you’ll need to keep using Personal Access Tokens for now.</p><p>If you use GitHub Enterprise Server, and this is important to you, please register your interest on the <a href="https://roadmap.octopus.com/c/273-octopus-deploy-app-for-github-enterprise-server">roadmap card</a>.</p></div>
<p>Happy deployments!</p>]]></content>
    </entry>
    <entry>
      <title>How to manage releases and rollbacks with Argo CD in Octopus Deploy</title>
      <link href="https://octopus.com/blog/manage-releases-rollbacks-argo-cd" />
      <id>https://octopus.com/blog/manage-releases-rollbacks-argo-cd</id>
      <published>2026-06-11T00:00:00.000Z</published>
      <updated>2026-06-11T00:00:00.000Z</updated>
      <summary>Learn why rollbacks aren't the best way to resolve production issues in every case, even though they are a couple of clicks to perform.</summary>
      <author>
        <name>Jubril Oyetunji, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>Most teams say they can roll back, but far fewer have actually tried it.</p>
<p>When they actually do, the plan is typically a runbook in a wiki, or the fact that, in theory, we can always revert to the last working commit. When you run Kubernetes, Argo CD is instrumental in keeping your resources in sync.</p>
<p>Argo CD is good at reconciling whatever’s in your Git repo to your cluster. When a deploy goes sideways, “roll back” turns into “revert the right commit, in the right repo, without breaking the other applications syncing alongside it”, which is fine on paper and miserable in practice.</p>
<p>In this post, we’ll explore how to manage releases in Octopus Deploy, how it integrates with Argo CD, and discuss, as well as implement, a rollback.</p>
<h2 id="what-argo-cd-doesnt-do-and-why-it-matters">What Argo CD doesn’t do (and why it matters)</h2>
<p>Argo CD is great at one job: take whatever’s in your Git repo and reconcile a cluster to match it. It’ll show you what’s drifted, when it last synced, and which resources are unhealthy.</p>
<p>However, what it deliberately doesn’t do is anything <em>above</em> the cluster.</p>
<p>There’s no concept of a release. Each <code>Application</code> is an independent entity, and its current state is whatever commit <code>HEAD</code> happens to be on. If Tuesday’s image tag turns out to be a regression, your options are: revert the commit in Git and wait for the next reconcile, or open the Argo CD UI and sync to a previous revision by hand.</p>
<p>More importantly, the processes around what constitutes a release that can be synced are something Argo simply wasn’t built for.</p>
<h3 id="how-octopus-manages-releases-with-argo-cd">How Octopus manages releases with Argo CD</h3>
<p>A release in Octopus is a versioned snapshot of three things:</p>
<ul>
<li>the deployment process (the steps it runs)</li>
<li>the variables those steps use, and</li>
<li>the package or image versions they reference.</li>
</ul>
<p>Once a release is created, it’s frozen. You can promote <code>0.0.2</code> through <code>Development</code>, then <code>Staging</code>, then <code>Production</code>, and each environment receives the <em>same</em> release, not whatever happens to sit at <code>HEAD</code> when you trigger the deployment.</p>
<p>With the Argo CD integration, that release model integrates into your existing GitOps setup. When you deploy a release in Octopus, you can have an Argo CD step that rewrites the image tags in your repo, commits the change with the credentials you’ve attached, then asks Argo CD to sync. Argo CD keeps doing the reconciliation while  Octopus keeps the lifecycle.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>This tutorial assumes you have:</p>
<ul>
<li>A working Kubernetes cluster (any flavour)</li>
<li><a href="https://kubernetes.io/docs/tasks/tools/"><code>kubectl</code></a> installed and pointed at the cluster</li>
<li>The <a href="https://cli.github.com/">GitHub CLI (<code>gh</code>)</a> installed and authenticated (<code>gh auth login</code>). We’ll use it to create the demo repo in one command</li>
<li>An Octopus Cloud (or self-hosted) instance, with the <a href="https://octopus.com/docs/argo-cd">Argo CD Gateway</a> installed in the cluster and an Argo CD instance already registered against Octopus. If you haven’t done that yet, follow the <a href="https://octopus.com/blog/connecting-aws-eks-argo-cd-to-octopus-cloud">Connecting AWS EKS Argo CD to Octopus Cloud</a> guide first; the same flow works for any cluster.</li>
<li>A Git <strong>Personal Access Token (PAT)</strong> scoped to your demo repository, Octopus will use it to push image-tag commits back</li>
</ul>
<h3 id="step-1-create-the-octopus-project">Step 1: Create the Octopus project</h3>
<ol>
<li>
<p>Sign in to Octopus and head to <strong>Projects</strong> > <strong>Add Project</strong>.</p>
</li>
<li>
<p>Give it a name (we’ll use <code>argo-demo</code>), pick <strong>Kubernetes</strong> as the deployment target, and answer <strong>Yes</strong> to <em>Are you using Argo CD?</em></p>
</li>
<li>
<p>Leave the lifecycle on <code>Default Lifecycle</code>.</p>
<figure><p><img src="/blog/img/manage-releases-rollbacks-argo-cd/octopus-add-new-project.png" alt="The new project dialog for a new argo-demo project"></p><figcaption>Creating the argo-demo project.</figcaption></figure>
</li>
<li>
<p>Hit <strong>Create Project</strong>.</p>
</li>
</ol>
<h3 id="step-2-add-the-argo-cd-step">Step 2: Add the Argo CD step</h3>
<p>You’ll land on the Process editor.</p>
<ol>
<li>Click <strong>Add Step</strong> and filter by <strong>Containers and Orchestration</strong> > <strong>Argo CD</strong> in the left sidebar. You’ll see two installed step templates:
<ul>
<li><strong>Update Argo CD Application Image Tags</strong>, which updates an image tag in your manifests and commits the change for Argo CD to sync</li>
<li><strong>Update Argo CD Application Manifests</strong>, which generate manifests from templates using Octopus variables, and commit those.</li>
</ul>
</li>
</ol>
<p>We’ll use the first one for this walkthrough.</p>
<figure><p><img src="/blog/img/manage-releases-rollbacks-argo-cd/octopus-add-step-argo-cd.png" alt="The add step dialog"></p><figcaption>The Argo CD category in the step picker. Both templates ship with Octopus once an Argo CD instance is registered.</figcaption></figure>
<h3 id="step-3-set-up-the-manifests-repository">Step 3: Set up the manifests repository</h3>
<p>Argo CD syncs from Git, and Octopus pushes image-tag updates back to that same repo, so before either side has anything to do, we need a repository with a workload manifest in it.</p>
<p>We’ll keep the layout small: one Deployment, one Service, and the Argo CD <code>Application</code> that wires the two systems together.</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="text"><code><span class="line"><span>argocd-octopus-demo/  </span></span>
<span class="line"><span>├── application.yaml      \# the Argo CD Application (apply once to the cluster)  </span></span>
<span class="line"><span>└── manifests/  </span></span>
<span class="line"><span>    └── app.yaml          \# workload Argo CD reconciles from this repo</span></span></code></pre>
<ol>
<li>
<p>Create the directory locally:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">mkdir</span><span style="color:#0000FF"> -p</span><span style="color:#A31515"> argocd-octopus-demo/manifests</span></span></code></pre>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">cd</span><span style="color:#A31515"> argocd-octopus-demo</span></span></code></pre>
</li>
<li>
<p>Write the workload manifest. We’re using <a href="https://hub.docker.com/r/nginxdemos/hello"><code>nginxdemos/hello:plain-text</code></a>, a tiny nginx that responds with a text page naming the pod that served the request. It’s a single-container app with no state, perfect for proving reconciliation worked end-to-end.</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#0000FF">cat > manifests/app.yaml &#x3C;&#x3C;'EOF'</span></span>
<span class="line"><span style="color:#800000">apiVersion</span><span style="color:#000000">: </span><span style="color:#0000FF">apps/v1</span></span>
<span class="line"><span style="color:#800000">kind</span><span style="color:#000000">: </span><span style="color:#0000FF">Deployment</span></span>
<span class="line"><span style="color:#800000">metadata</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  name</span><span style="color:#000000">: </span><span style="color:#0000FF">hello</span></span>
<span class="line"><span style="color:#800000">  labels</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">    app</span><span style="color:#000000">: </span><span style="color:#0000FF">hello</span></span>
<span class="line"><span style="color:#800000">spec</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  replicas</span><span style="color:#000000">: </span><span style="color:#098658">2</span></span>
<span class="line"><span style="color:#800000">  selector</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">    matchLabels</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">      app</span><span style="color:#000000">: </span><span style="color:#0000FF">hello</span></span>
<span class="line"><span style="color:#800000">  template</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">    metadata</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">      labels</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">        app</span><span style="color:#000000">: </span><span style="color:#0000FF">hello</span></span>
<span class="line"><span style="color:#800000">    spec</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">      containers</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#000000">        - </span><span style="color:#800000">name</span><span style="color:#000000">: </span><span style="color:#0000FF">hello</span></span>
<span class="line"><span style="color:#800000">          image</span><span style="color:#000000">: </span><span style="color:#0000FF">nginxdemos/hello:plain-text</span></span>
<span class="line"><span style="color:#800000">          ports</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#000000">            - </span><span style="color:#800000">containerPort</span><span style="color:#000000">: </span><span style="color:#098658">80</span></span>
<span class="line"><span style="color:#800000">          resources</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">            requests</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">              cpu</span><span style="color:#000000">: </span><span style="color:#0000FF">25m</span></span>
<span class="line"><span style="color:#800000">              memory</span><span style="color:#000000">: </span><span style="color:#0000FF">32Mi</span></span>
<span class="line"><span style="color:#000000">---</span></span>
<span class="line"><span style="color:#800000">apiVersion</span><span style="color:#000000">: </span><span style="color:#0000FF">v1</span></span>
<span class="line"><span style="color:#800000">kind</span><span style="color:#000000">: </span><span style="color:#0000FF">Service</span></span>
<span class="line"><span style="color:#800000">metadata</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  name</span><span style="color:#000000">: </span><span style="color:#0000FF">hello</span></span>
<span class="line"><span style="color:#800000">spec</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  type</span><span style="color:#000000">: </span><span style="color:#0000FF">ClusterIP</span></span>
<span class="line"><span style="color:#800000">  selector</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">    app</span><span style="color:#000000">: </span><span style="color:#0000FF">hello</span></span>
<span class="line"><span style="color:#800000">  ports</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#000000">    - </span><span style="color:#800000">port</span><span style="color:#000000">: </span><span style="color:#098658">80</span></span>
<span class="line"><span style="color:#800000">      targetPort</span><span style="color:#000000">: </span><span style="color:#098658">80</span></span>
<span class="line"><span style="color:#800000">      protocol</span><span style="color:#000000">: </span><span style="color:#0000FF">TCP</span></span>
<span class="line"><span style="color:#0000FF">EOF</span></span></code></pre>
</li>
<li>
<p>Initialize the repo, create a public GitHub repo from it, and push the first commit:</p>
</li>
</ol>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">git</span><span style="color:#A31515"> init</span><span style="color:#0000FF"> -b</span><span style="color:#A31515"> main</span></span></code></pre>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">git</span><span style="color:#A31515"> add</span><span style="color:#A31515"> .</span></span></code></pre>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">git</span><span style="color:#A31515"> commit</span><span style="color:#0000FF"> -m</span><span style="color:#A31515"> "chore: init"</span></span></code></pre>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">gh</span><span style="color:#A31515"> repo</span><span style="color:#A31515"> create</span><span style="color:#000000"> &#x3C;</span><span style="color:#A31515">your-or</span><span style="color:#000000">g></span><span style="color:#A31515">/argocd-octopus-demo</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --public</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --description</span><span style="color:#A31515"> "Argo CD + Octopus Deploy demo manifests"</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --source=.</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --push</span></span></code></pre>
<p>With the repo live, we can write the Argo CD <code>Application</code> that points at it.</p>
<h3 id="step-4-scope-the-argo-cd-application-with-annotations">Step 4: Scope the Argo CD Application with annotations</h3>
<p>The Octopus gateway pulls the full list of Argo CD Applications from your cluster, but it doesn’t yet know <em>which</em> of them belong to <em>this</em> Octopus project. You can tell it with three annotations on the Argo Application:</p>
<ul>
<li><code>argo.octopus.com/project[.&#x3C;source-name>]</code></li>
<li><code>argo.octopus.com/environment[.&#x3C;source-name>]</code></li>
<li><code>argo.octopus.com/tenant[.&#x3C;source-name>]</code></li>
</ul>
<p>The <code>.&#x3C;source-name></code> suffix matches <code>spec.source.name</code> on the Application, which lets you scope different sources of a multi-source Application to different Octopus projects.</p>
<p>For our <code>argo-demo</code> project and <code>Development</code> environment, an annotated <code>Application</code> looks like this:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#800000">apiVersion</span><span style="color:#000000">: </span><span style="color:#0000FF">argoproj.io/v1alpha1</span></span>
<span class="line"><span style="color:#800000">kind</span><span style="color:#000000">: </span><span style="color:#0000FF">Application</span></span>
<span class="line"><span style="color:#800000">metadata</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  name</span><span style="color:#000000">: </span><span style="color:#0000FF">argo-demo</span></span>
<span class="line"><span style="color:#800000">  namespace</span><span style="color:#000000">: </span><span style="color:#0000FF">argocd</span></span>
<span class="line"><span style="color:#800000">  annotations</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">    argo.octopus.com/project.argo-demo-source</span><span style="color:#000000">: </span><span style="color:#0000FF">argo-demo</span></span>
<span class="line"><span style="color:#800000">    argo.octopus.com/environment.argo-demo-source</span><span style="color:#000000">: </span><span style="color:#0000FF">development</span></span>
<span class="line"><span style="color:#800000">spec</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">  project</span><span style="color:#000000">: </span><span style="color:#0000FF">default</span></span>
<span class="line"><span style="color:#800000">  source</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">    repoURL</span><span style="color:#000000">: </span><span style="color:#0000FF">https://github.com/&#x3C;your-org>/argocd-octopus-demo.git</span></span>
<span class="line"><span style="color:#800000">    targetRevision</span><span style="color:#000000">: </span><span style="color:#0000FF">HEAD</span></span>
<span class="line"><span style="color:#800000">    path</span><span style="color:#000000">: </span><span style="color:#0000FF">manifests</span></span>
<span class="line"><span style="color:#800000">    name</span><span style="color:#000000">: </span><span style="color:#0000FF">argo-demo-source</span></span>
<span class="line"><span style="color:#800000">  destination</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">    server</span><span style="color:#000000">: </span><span style="color:#0000FF">https://kubernetes.default.svc</span></span>
<span class="line"><span style="color:#800000">    namespace</span><span style="color:#000000">: </span><span style="color:#0000FF">default</span></span>
<span class="line"><span style="color:#800000">  syncPolicy</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">    automated</span><span style="color:#000000">:</span></span>
<span class="line"><span style="color:#800000">      prune</span><span style="color:#000000">: </span><span style="color:#0000FF">true</span></span>
<span class="line"><span style="color:#800000">      selfHeal</span><span style="color:#000000">: </span><span style="color:#0000FF">true</span></span></code></pre>
<p>The two annotations there tie this Application to the <code>argo-demo</code> Octopus project, scoped to the <code>development</code> environment. The suffix <code>.argo-demo-source</code> matches the <code>spec.source.name</code> field below it.</p>
<p>Apply it:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">kubectl</span><span style="color:#A31515"> apply</span><span style="color:#0000FF"> -f</span><span style="color:#A31515"> application.yaml</span></span></code></pre>
<p>Within a few seconds, the Octopus gateway picks up the new annotations, and the Application appears in the project’s deployment preview.</p>
<h3 id="step-5-preview-the-deployment">Step 5: Preview the deployment</h3>
<p>Back in Octopus, in the Process tab, click on the  step you just added.</p>
<figure><p><img src="/blog/img/manage-releases-rollbacks-argo-cd/octopus-deployment-preview.png" alt="Deployment preview screen"></p><figcaption>Octopus has discovered the argo-demo Application via the annotations</figcaption></figure>
<p>Annotations have been discovered, but flags that the source repo is missing Git credentials. Click View Applications to see what was matched.</p>
<p>The deployment preview opens. You should see the <code>argo-demo</code> application listed under the <code>Rollback</code> Argo CD instance, scoped to the <code>Development</code> environment, with the source repo URL underneath.</p>
<h3 id="step-6-connect-git-credentials">Step 6: Connect Git credentials</h3>
<p>Octopus needs Git credentials so the step can push the new image-tag commit.</p>
<ol>
<li>
<p>Click the <strong>Connect Git Credential</strong> dropdown next to the repo URL.</p>
<figure><p><img src="/blog/img/manage-releases-rollbacks-argo-cd/octopus-git-credential-dropdown.png" alt="The connect Git credential dropdown"></p><figcaption>Two paths: spin up a new credential in Octopus from a PAT, or reuse one you’ve already attached to this space</figcaption></figure>
</li>
<li>
<p>Pick <strong>Create a new Git credential</strong> (or <strong>Connect an existing Git credential</strong> if you’ve already added one to the space).</p>
</li>
</ol>
<p>Once the credential is attached, the warning on the deployment preview clears and the source shows up as ready to update.</p>
<h3 id="step-7-configure-the-step">Step 7: Configure the step</h3>
<p>Back on the step, fill in the rest:</p>
<figure><p><img src="/blog/img/manage-releases-rollbacks-argo-cd/octopus-step-config.png" alt="The process editor screen with the changes"></p><figcaption>Confirm your step settings look good</figcaption></figure>
<p>The key fields:</p>
<ul>
<li><strong>Step Name</strong>, leave as the default unless you have multiple Argo CD steps in one process.</li>
<li><strong>Container Images</strong>, click <strong>Add</strong> to declare which container image(s) this step should bump. You’ll point it at a <a href="https://octopus.com/docs/projects/steps/package-references">package/container reference</a> so Octopus knows which image and which feed to pull tags from.</li>
<li><strong>Git Commit Settings</strong>, the commit author/email Octopus uses when it pushes the tag update.</li>
<li><strong>Commit Message</strong>, supports Octopus variables, so something like <code>chore: bump image to #{Octopus.Action.Package[hello].PackageVersion}</code> is a good default.</li>
</ul>
<p>Save the step.</p>
<h3 id="step-8-create-a-release">Step 8: Create a release</h3>
<p>Head to the project dashboard, and you should now see the <strong>Create Release</strong> prompt.</p>
<figure><p><img src="/blog/img/manage-releases-rollbacks-argo-cd/octopus-project-dashboard.png" alt="The project dashboard"></p><figcaption>The project dashboard once the deployment process has at least one step. Hit Create Release.</figcaption></figure>
<p>On the release form, give it a version (Octopus auto-suggests 0.0.1) and click Save.</p>
<figure><p><img src="/blog/img/manage-releases-rollbacks-argo-cd/octopus-create-release.png" alt="Create release page"></p><figcaption>Create release page</figcaption></figure>
<p>Release notes are optional but recommended for anything you’d want a changelog entry for later.</p>
<h3 id="step-9-deploy-to-development">Step 9: Deploy to Development</h3>
<p>Pick <strong>Deploy to Development</strong> on the release page. Octopus kicks off the deployment, and the task log streams the three sub-steps in real time:</p>
<ol>
<li><strong>Update Argo CD Application Image Tags</strong>, Octopus rewrites the image tag in the manifest and pushes the commit</li>
<li><strong>Triggering Argo CD application syncs</strong>, Octopus asks Argo CD to sync the Application</li>
<li><strong>Wait for Argo CD Application Health</strong>, Octopus polls the Application until it reports Healthy</li>
</ol>
<p>When all three turn green, you’ll see a completion line in the task log, and a <strong>Deploy to Staging…</strong> button appears at the top right, ready for the next environment in the lifecycle.</p>
<figure><p><img src="/blog/img/manage-releases-rollbacks-argo-cd/octopus-deploy-complete.png" alt="Deployment task screen"></p><figcaption>Deployment completes in just over 90 seconds</figcaption></figure>
<p>You can verify the rollout landed on the cluster side too:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">kubectl</span><span style="color:#0000FF"> -n</span><span style="color:#A31515"> default</span><span style="color:#A31515"> get</span><span style="color:#A31515"> deploy</span><span style="color:#A31515"> hello</span><span style="color:#0000FF"> -o</span></span>
<span class="line"><span style="color:#001080">jsonpath</span><span style="color:#000000">=</span><span style="color:#A31515">'{.spec.template.spec.containers[0].image}{"\n"}'</span></span></code></pre>
<h3 id="step-10-rolling-back-redeploying-a-previous-release">Step 10: Rolling back: redeploying a previous release</h3>
<p>The deploy half of the demo is done. The other half is the one that actually earns its keep: a release that turns out to be bad, and how Octopus takes you back to the previous one without anyone opening a terminal.</p>
<p>To simulate it, hit <strong>Create Release</strong> again, save as <code>0.0.3</code>, and deploy it to <code>Development</code>. Imagine the new image is broken (regression, missing env var, whatever). To roll back, head to <strong>Releases</strong> in the project’s left sidebar.</p>
<figure><p><img src="/blog/img/manage-releases-rollbacks-argo-cd/octopus-all-releases.png" alt="Releases screen"></p><figcaption>Every release Octopus has ever cut for this project, newest first. Each one is a versioned artifact you can revisit and redeploy. To roll back, open the last known-good release, 0.0.2</figcaption></figure>
<p>Click <code>0.0.2</code> to open it, then look at the lifecycle table. The <code>Development</code> row will show its previous deployment with a <strong>Redeploy…</strong> button.</p>
<figure><p><img src="/blog/img/manage-releases-rollbacks-argo-cd/octopus-redeploy-002.png" alt="The redeploy button is shown next to the release"></p></figure>
<p>Click <strong>Redeploy…</strong>, confirm, and the same three sub-steps run again.</p>
<p>Octopus rewrites the image tag in Git back to the one <code>0.0.2</code> used, commits it, asks Argo CD to sync, then waits until the Application reports Healthy.</p>
<p>When the task completes, the project dashboard shows <code>0.0.2</code> back in Development.</p>
<figure><p><img src="/blog/img/manage-releases-rollbacks-argo-cd/octopus-rollback-healthy.png" alt="Project dashboard screen"></p><figcaption>The dashboard after the rollback shows a healthy application.</figcaption></figure>
<h2 id="when-to-roll-back-vs-when-to-roll-forward">When to roll back vs when to roll forward</h2>
<p>For this demo, rolling back was a single click. That doesn’t mean rolling back is always the right call. Rollbacks have a way of failing that isn’t always talked about.</p>
<p>They assume the rest of the system is in a state that the old version can still talk to. Most of the time it is. Sometimes it very much isn’t.</p>
<p>Take, for instance, a database. If a bad release adds a new column and your users have been generating data through that path for an hour, rolling the application code back doesn’t unwind the schema or the data. The old app meets the new schema and either ignores fields it doesn’t know about (best case) or crashes on them (worst case)</p>
<p>Refactoring databases buys you flexibility. If schema changes go in through additive, backward-compatible migrations (add columns nullable, write to both old and new fields for a release cycle, then drop the old in a later release), rollbacks stay safe much longer.</p>
<h2 id="make-rollbacks-boring">Make rollbacks boring</h2>
<p>If you made it this far, you’ve got the full loop: an Argo CD <code>Application</code> scoped to an Octopus project with annotations, a deploy that flows from a click down through Git into your cluster, and a redeploy that takes you back to a known-good release the same way.</p>
<p>Argo CD keeps reconciling, your manifests stay the source of truth, and Octopus wraps versioning, approvals, audit, and that redeploy button around them. The rollback nobody had tested becomes a button two engineers know how to press.</p>
<p>Beyond manual rollbacks, Octopus enables you to build out full lifecycles on your deployments, so this can all be automated conditionally.</p>
<p>Extend the pipeline to your real environments using the <a href="https://octopus.com/docs/argo-cd">Argo CD integration docs</a>, or if you don’t have an Octopus instance yet, <a href="https://octopus.com/start">start a free trial</a> and run this end-to-end on your own cluster.</p>]]></content>
    </entry>
    <entry>
      <title>Continuous Delivery Office Hours Ep.5: Delivering database changes</title>
      <link href="https://octopus.com/blog/continuous-delivery-office-hours-e5" />
      <id>https://octopus.com/blog/continuous-delivery-office-hours-e5</id>
      <published>2026-06-09T00:00:00.000Z</published>
      <updated>2026-06-09T00:00:00.000Z</updated>
      <summary>Learn why database deployments are different from application deployments, and what techniques you can use to make them worry-free.</summary>
      <author>
        <name>Steve Fenton, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>In the previous episode, we talked about <a href="/blog/continuous-delivery-office-hours-e4">different service design approaches</a>. This time, we dive deeper into database changes.</p>
<p>Even for teams that can effortlessly deploy their application code, database changes can be more stressful. Changing a schema is a high-stakes operation, and there are many ways to do it badly.</p>
<p>Read on to find out why database deployments are different from application deployments, and what techniques you can use to make them worry-free.</p>
<h2 id="watch-the-episode">Watch the episode</h2>
<p>You can watch the episode below, or read on to find some of the key discussion points.</p>
<p><a href="https://www.youtube.com/watch?v=_rHyP_RZbQc">Watch Continuous Delivery Office Hours Ep.5</a></p>
<h2 id="why-are-databases-different">Why are databases different</h2>
<p>When you change your application’s code and discover an issue, it’s trivial to revert to the previous version. In rare cases where a change affects your data, there may be cleanup to do.</p>
<p>You can use timestamps and a one-off process to fix those rare data-mess-up instances, but they hint at something fundamentally different about database updates. They have different levels and types of risk associated with them.</p>
<p>If you think backups will save you from a bad database deployment, you haven’t yet tried it. Sure, they prevent total data loss, but between your backup and your fix, the data moved. Often by a lot.</p>
<p>There are techniques to apply transactions from the backup up to the point of failure, but if your change caused an issue you needed to roll back, you probably don’t want to apply those transactions automatically. Welcome to the “data remediation project”.</p>
<p>If you added a new column or table as part of your database change and you need to roll back, you have to decide what to do with any data in those tables. Do you forget it, or do you need to keep hold of it and reapply it later when you make a new attempt to extend the schema?</p>
<p>Modern software teams prefer fix-forward for application issues, which are reasonably easy to roll back. Databases take rollbacks to another level.</p>
<h2 id="crucial-modernization-steps">Crucial modernization steps</h2>
<p>Imagine you met a friend for lunch and they told you they store they application’s source code on a network share, rather than in version control. You’d think it was a joke, and when you realize it’s not, you’d form a strong opinion about the kind of sloppy outfit they must be running.</p>
<p>When we meet for lunch, what are you going to tell me about your database schema and static data? Please tell me it’s all in version control, not on a network share.</p>
<p>You should make all database changes by updating the files in version control and deploying them like you would your application code. You progress the change through environments to ensure it works, and you avoid embarrassment caused by the application failing because someone forgot to add the new column in production.</p>
<p>There are further choices to make, which we’ll cover next, but failing to version-control your database is unforgivable.</p>
<h2 id="state-based-vs-migration-based-approaches">State-based vs migration-based approaches</h2>
<p>On to the first choice for your database project. Do you make state-based or migration-based updates?</p>
<p>State-based schemas describe the desired state of the database. It will list each table with its columns, indexes, and relationships. You use a model-based tool to deploy the database, which compares your current state with the desired state and applies the changes for you.</p>
<p>Some state-based tools convert the differences into standard database scripts, like <code>ALTER TABLE...</code> scripts. Others perform migrations by creating a new table with the changes and moving the data into it. This is important if you’re using a technology like replication, which prevents the model migration mechanism from working.</p>
<p>The alternative to state-based database updates is migration-based updates, where you write your own <code>ALTER TABLE</code> scripts. You keep all your scripts in version control and use a tool that applies them in order and tracks when each was applied, preventing the same script from being applied twice to the same database.</p>
<p>The main difference between the two is procedural. You can code-review the migration scripts on demand, but you’ll need to review state-based migration scripts after your tools generate the implementation plan, which can make the review task more of a large batch.</p>
<h2 id="tooling-and-automation">Tooling and automation</h2>
<p>Whichever approach you use, tooling helps it work. Many of our customers use Redgate tools as part of their deployment process to manage database schema changes, and there’s value in leaning on a tool written by folks who care deeply about the problem and how to solve it.</p>
<p>Crucially, you shouldn’t make any database changes outside of tool-based automation.</p>
<h2 id="test-data-management">Test data management</h2>
<p>Once you’ve automated your database schema and static data, it’s worth considering your test data. When automated or manual acceptance tests fail, it’s usually because someone unwittingly messed up the data. Prior test runs often leave data in an inconsistent state, especially if a test failure halts the run.</p>
<p>You can resolve this issue by automating your test data setup. Not only does this make it easy to reset the data during your build and test cycle, but it also lets you provide a self-service runbook for the test team to reset the data in their test environment whenever they need to.</p>
<p>There’s an up-front investment in this, but I can promise that it takes fewer hours than fixing your test data a few times.</p>
<h2 id="database-refactoring-patterns">Database refactoring patterns</h2>
<p>The final thought to ponder concerns the steps you take in changing your database schema. When you’re in the habit of deploying your database and application in the same release process, you start to depend on this change coordination.</p>
<p>You delete a column from the database and immediately deploy the application, with all references to the deleted column removed. It looks like it works smoothly, but it’s a trap.</p>
<p>Imagine you had a critical bug in the application and had to redeploy the previous version. Now you can’t, because the previous version will try to read from a column that doesn’t exist. You no longer have a quick, easy back out plan, since you have to re-add the column, and you’ll also need data to put in it.</p>
<p>This approach also prevents you from making seamless deployments, as even if you progressively roll out the application version, the old version will error out due to the database change. You may deploy in the opposite order, application first, then database. You’ll discover the same problem with new columns that the latest version expects to find in the database.</p>
<p>You need to decouple database and application deployments, and there’s a whole book on the topic, called Refactoring Databases (Ambler, Sadalage). You can start by following the expand/contract pattern, which splits updates into steps. The principle is that you don’t delete a column until the production application has no reads or writes. You add a column and don’t reference it in your code until you deploy to production.</p>
<p>This means you can run the current version and the new version of the software against the same database, which means you can progressively roll out the new version and redeploy the prior version without touching the database.</p>
<h2 id="databases-are-only-as-hard-as-you-make-them">Databases are only as hard as you make them</h2>
<p>The database is high-risk, which is why updating it can be scary. I hope you’ve found this post full of practical advice for making database deployments robust and stress-free.</p>
<p>Database deployments, like application deployments, should be a happy time. You should be celebrating the new features and enhancements you’ve delivered to your users, not biting your nails and worrying that something’s about to go horribly wrong.</p>
<p>Happy deployments!</p>
<div class="hint"><p>Continuous Delivery Office Hours is a series of conversations about software delivery, with Tony Kelly, Bob Walker, and Steve Fenton.</p><p>You can find more episodes on <a href="https://www.youtube.com/playlist?list=PLAGskdGvlaw3CrxkUOAMmiy928lr5D4oh">YouTube</a>, <a href="https://podcasts.apple.com/us/podcast/continuous-delivery-office-hours/id1872101651">Apple Podcasts</a>, and <a href="https://pca.st/hwjaox59">Pocket Casts</a>.</p></div>]]></content>
    </entry>
    <entry>
      <title>Announcing the Octopus Intelligence Agent App integration with GitHub</title>
      <link href="https://octopus.com/blog/octopus-intelligence-integration-with-github-agent-hq" />
      <id>https://octopus.com/blog/octopus-intelligence-integration-with-github-agent-hq</id>
      <published>2026-06-04T00:00:00.000Z</published>
      <updated>2026-06-04T00:00:00.000Z</updated>
      <summary>New milestone for the Octopus Deploy and GitHub partnership: Octopus Intelligence is now available in GitHub Agent HQ</summary>
      <author>
        <name>Madalina Iosif, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>For years, developer productivity focused on writing code faster. Today, speed is just the default with agents such as Copilot contributing huge amounts of code and generally being the top contributor by commit volume in many enterprises. The challenge is that AI code contributions mean that the focus is now shifting towards how teams understand, manage, secure, and deploy software throughout its entire lifecycle, while having to handle multiple agents along the way.</p>
<p>That’s why the strategic partnership between Octopus Deploy and GitHub is so significant.</p>
<p>While many organizations view AI as a coding assistant, GitHub and Octopus are helping teams unlock something more powerful: an AI-enabled software delivery platform that connects development, deployment, operations, and most importantly governance into a single intelligent workflow.</p>
<p>As partners, we made steps in this direction by contributing to major innovations such as Copilot Extensions back in 2024, then <a href="https://github.blog/news-insights/product-news/your-stack-your-rules-introducing-custom-agents-in-github-copilot-for-observability-iac-and-security/">Custom Agents</a> in 2025 and now being again a <a href="https://github.blog/changelog/2026-06-02-extend-github-with-agent-apps/">launch partner for Agent Apps within GitHub</a>.</p>
<h2 id="no-more-context-switching-and-more-control-over-your-agents">No more context switching and more control over your agents</h2>
<p>The launch of <a href="https://github.blog/news-insights/company-news/welcome-home-agents/">Agent HQ at GitHub Universe</a> made it already possible to access coding agents from Anthropic, OpenAI, Google, Cognition and xAI directly within the GitHub interface. Because regardless of how powerful they are, more agents will not improve efficiency, but rather generate complexity and loss of control unless properly governed.</p>
<p>But writing code is only one part of the journey. The real challenge for enterprise teams begins after the pull request is merged:</p>
<ul>
<li>Understanding deployment status</li>
<li>Managing complex release pipelines and troubleshooting production issues</li>
<li>Coordinating multi-tenant deployments and identifying issues while operating software at scale</li>
<li>Maintaining security and compliance controls</li>
</ul>
<p>Historically, these activities have required developers to switch between multiple tools, dashboards, and operational systems. But every context switch creates friction, every disconnected workflow slows delivery or, even more importantly, delays recovery when something goes wrong.</p>
<p>This is precisely the problem Octopus and GitHub are solving together by bringing the Octopus Intelligence to Agent HQ . When deployment information becomes accessible through natural language, organizations can unlock knowledge that was previously only accessible to specialist teams and take action quicker when necessary, avoiding downtime.</p>
<blockquote>
<p>“The future of software development won’t be shaped by any single agent, but by an ecosystem of specialized ones. Our partners are defining what that looks like by embedding Agent apps into the GitHub workflows where developers build and ship.” Mario Rodriguez, Chief Product Officer at GitHub</p>
</blockquote>
<p><img src="/blog/img/octopus-intelligence-integration-with-github-agent-hq/agent-hq-presentation.png" alt="Microsoft build - Github Agent HQ presentation"></p>
<p>By combining the Octopus MCP Server with specialized skills, the Octopus Intelligence Agent enables a seamless, automated workflow. This integration allows the agent to interact directly with Octopus Deploy from the GitHub interface, surfacing key data and executing tasks as directed.</p>
<p>Here is an example use case for the Octopus Intelligence Agent, directly within GitHub Agent HQ:</p>
<ul>
<li>Show the deployment status of a PR, and when it was deployed to a specific tenant</li>
<li>Investigate and diagnose why it has failed</li>
<li>Use the appropriate runbook to fix the failure</li>
<li>Redeploy to tenant</li>
</ul>
<p>See it in action! The demo video below shows how the Octopus Intelligence agent helps diagnose and recover from failed tenant deployments without leaving GitHub. When reviewing a PR, you can check the deployment status across tenants, investigate failures, and trigger the appropriate runbook to fix the root cause - then redeploy directly to the affected tenant once resolved.</p>
<p><a href="https://www.youtube.com/watch?v=OtneiOgcD1U">Demo video</a></p>
<h2 id="our-strategic-partnership">Our strategic partnership</h2>
<p>Octopus Deploy and GitHub share a long standing committed partnership, focused on shaping a best-of-breed enterprise CI/CD landscape.</p>
<p>GitHub is where developers collaborate, build, and innovate. Octopus is where organizations orchestrate complex deployments across hybrid infrastructure, including Kubernetes, cloud-native platforms, or legacy systems, with enterprise governance in mind.</p>
<p>And we are not stopping there. By aligning our GitHub and Microsoft partnerships, we are able to bring the next level of DevOps innovation to our customers.</p>
<p>Together, we create a connected software delivery experience that helps teams move faster and securely despite the increasing complexity.</p>
<p>Learn more about our partnership with Microsoft and GitHub:</p>
<p><a href="https://www.youtube.com/watch?v=GwMw_IdB3_4">Microsoft case study video</a></p>
<p>If you are interested in using Octopus together with GitHub, you can now <a href="https://billing.octopus.com/free-signup">register for a free account</a> - the agentic features are included!  Octopus Deploy can be purchased via the <a href="https://marketplace.microsoft.com/en-us/product/saas/octopusdeployptyltd1664464418055.octopus_cloud_saas_offer?tab=overview">Microsoft Marketplace</a> and is Azure benefit eligible.</p>
<p>Happy deployments!</p>]]></content>
    </entry>
    <entry>
      <title>Automatic superseded task cleanup</title>
      <link href="https://octopus.com/blog/superseded-tasks" />
      <id>https://octopus.com/blog/superseded-tasks</id>
      <published>2026-05-22T00:00:00.000Z</published>
      <updated>2026-05-26T00:00:00.000Z</updated>
      <summary>You can now configure Octopus to expedite deployment tasks by cancelling superseded ones.</summary>
      <author>
        <name>Frank Lin, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>Imagine your CI/CD pipeline kicks off a deployment, but before it starts, two more commits land and trigger more deployments. Wouldn’t it be nice if Octopus only ran the last one and skipped the first two?</p>
<h2 id="what-are-superseded-tasks">What are superseded tasks?</h2>
<p>A task is superseded when a newer task makes it redundant. For example, if three deployments of the same project and environment are queued, the first two are superseded once the third is queued, as the third deployment will leave the environment in the desired state.</p>
<p>As AI coding tools push commit and PR volume upwards, a growing queue of superseded deployments becomes more pronounced</p>
<p>Instead of letting those redundant tasks run to completion, Octopus can automatically cancel them to expedite the latest task. The result is faster deployments, shorter feedback cycles and less resource use.</p>
<h2 id="how-does-it-work">How does it work?</h2>
<p>When a new deployment task is queued, Octopus checks for other queued tasks that target the same project and environment. If it finds tasks that are waiting to start, it cancels them and marks them as superseded.</p>
<figure><p><img src="/blog/img/superseded-tasks/cancel-task-audit.png" alt="Canceled task audit trail"></p></figure>
<p>A few important things to note:</p>
<ul>
<li>Only <strong>queued</strong> tasks that are yet to start are considered for cancellation. If a task has already started executing, Octopus won’t interrupt it mid-deployment.</li>
<li>Tasks with skipped steps or included/excluded targets are not considered for cancellation since it’s difficult to ensure the intended changes are still flowing through the deployment pipeline</li>
<li>Superseded tasks are cancelled, not deleted. You can still see them in the task log with a clear explanation of what happened and which task superseded them.</li>
<li>This applies to deployments and runbook runs where the outcome of the newer task makes the older one irrelevant.</li>
</ul>
<h2 id="variation---cancel-running-tasks">Variation - cancel running tasks</h2>
<p>As we were developing the <a href="https://octopus.com/docs/argo-cd/steps#pull-request-merged">Argo CD integration</a>, we realized that Octopus could potentially have many deployments paused while waiting for pull request approval for the same project and environment. Once the latest pull request merges, the user would need to cancel the previous deployments manually.</p>
<p>This is why we’ve added the option to also cancel running tasks that are waiting for external events like pull request merges and manual intervention.</p>
<h2 id="how-do-i-configure-this">How do I configure this?</h2>
<p>These settings are active by default for all new projects, you can customize the behavior for each project’s deployment process or runbook by editing their respective settings.</p>
<figure><p><img src="/blog/img/superseded-tasks/cancel-task-settings.png" alt="Cancel task settings"></p></figure>
<h2 id="learn-more">Learn more</h2>
<p>For the full details on how superseded tasks work, head to the <a href="https://octopus.com/docs/tasks/superseded-tasks">official documentation</a>.</p>
<p>Happy deployments!</p>]]></content>
    </entry>
    <entry>
      <title>Stop maintaining what your vendor now ships</title>
      <link href="https://octopus.com/blog/stop-maintaining-what-your-vendor-now-ships" />
      <id>https://octopus.com/blog/stop-maintaining-what-your-vendor-now-ships</id>
      <published>2026-05-21T00:00:00.000Z</published>
      <updated>2026-05-21T00:00:00.000Z</updated>
      <summary>Most practitioners have a custom solution running somewhere that a vendor has since solved in their product. The hard part is knowing when it's time to let it go.</summary>
      <author>
        <name>Matthew Allford, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>There’s a version of technical debt nobody talks about enough, and I’m guilty of having created plenty of it across my years in IT. I’m talking about the custom solutions you build to fill a gap your vendor hasn’t solved yet, and that you’re still running years later, long after they did.</p>
<p>Most practitioners will be familiar with this. A script, a Lambda, a homegrown integration that filled a real gap at the right moment. It was built for good reasons, shipped, and is still running somewhere in your stack. Knowing when it’s time to let it go is the hard part.</p>
<p>That moment usually comes when your vendor catches up, and it happens much more often than most people realize.</p>
<h2 id="why-do-we-build-our-own-things">Why do we build our own things?</h2>
<p>A friend recently posted something on LinkedIn that grabbed my attention:</p>
<figure><p><img src="/blog/i/x/stop-maintaining-what-your-vendor-now-ships/sam-linkedin-post.png" alt="File this under: Why did this take so long to exist?! AWS Direct Connect now publishes BGP session health metrics natively in CloudWatch. Time to kill off my Lambda that&#x27;s been creating these metrics using boto3." loading="lazy" srcset="/blog/i/600/stop-maintaining-what-your-vendor-now-ships/sam-linkedin-post.webp 600w, /blog/i/1000/stop-maintaining-what-your-vendor-now-ships/sam-linkedin-post.webp 1000w, /blog/i/2000/stop-maintaining-what-your-vendor-now-ships/sam-linkedin-post.webp 1100w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="1100" height="604">
</p><figcaption>Source: <a href="https://www.linkedin.com/posts/samjamesb_file-this-under-why-did-this-take-so-long-share-7449227391018663936-owdW?utm_source=share&#x26;utm_medium=member_desktop&#x26;rcm=ACoAAB54NGUBJjvwtU0y7YqTrUL84vlpROU5A0Q">Sam Bentley</a></figcaption><p></p></figure>
<p>This is a story most practitioners will recognize. You’re working with a tool you depend on, but it might lack functionality for something you need to achieve today, so you build around it. A script, a wrapper, in Sam’s case, a Lambda, whatever it might be. You have a real need today, the vendor doesn’t have an answer yet, and may not for many months or years, so you solve it yourself. There’s nothing wrong with this approach; it’s pragmatic engineering, and the right call.</p>
<p>Vendors can’t prioritize every use case that every customer might have. Their roadmap has to serve a broad set of customers, which means your specific requirement might be well understood - and something the vendor wishes to solve - but it may not be next in line on their list of priorities. So you build something that fits your context and environment, ship it, and move on to the next problem.</p>
<h2 id="platform-engineers-will-recognize-this">Platform Engineers will recognize this</h2>
<p>This is especially common for platform teams. They’re often the ones filling gaps across a whole stack of tools being used by hundreds or thousands of developers and application teams. Most Platform Engineers will resonate with building integrations, abstractions, and internal tooling so the rest of the organization doesn’t have to think about the seams between tools and has a great experience.</p>
<p>At Octopus Deploy, we see this across our customer base. One example that comes to mind is a customer who needed to ensure a specific deployment step was <strong>always</strong> present and enabled across thousands of projects. The problem will likely be familiar — developers, eager to get past a failing step and move on with their deployment, would disable or remove it entirely. In this case, skipping the step introduced a serious risk to the organization, as the step was critical to their auditing processes.</p>
<p>Octopus didn’t have a great built-in answer for this at the time, so the customer wrote a script that interrogated the API, identified non-compliant projects, and flagged them for manual review. It worked for the purposes it was built for, but it was reactive, needed maintenance, and relied on one or two people who understood it well enough to keep it running if something broke or needed tweaking.</p>
<p>We’ve since introduced Policies as part of Platform Hub, an enforcement engine that handles exactly the scenario the customer was looking for, baked into the product proactively.</p>
<p>In both cases I’ve shared, the build was the right call. But eventually the vendor caught up, and that changed things.</p>
<h2 id="the-slow-drift-problem">The slow drift problem</h2>
<p>When you ship a custom solution, the problem is solved, and life moves on. But quietly over time, it becomes part of the furniture. It runs on a schedule, or it sits in a repo somewhere, and nobody questions it because it works. The person who wrote it might still be around, or they might not. Either way, the context that existed when it was built starts to fade.</p>
<p>Meanwhile, the vendors you depend on don’t stand still. They’re shipping new features, adapting to the market, and building capabilities based on the feedback their customers have been giving them. That script you wrote, or the Lambda you set up two years ago to solve a problem they didn’t have an answer for? There’s a decent chance you weren’t the only one with that problem to solve, and the vendor may have an answer for it now. But if you’re not paying attention to what they’re shipping, you’d never know.</p>
<p>The Octopus example is a good illustration of this. The API script that checked for compliant deployment steps across thousands of projects didn’t stop working, and nobody rushed to replace it. But when Policies shipped as part of Platform Hub, the situation changed. The same requirement was now handled in the product, proactively, and without the maintenance overhead. The custom solution wasn’t immediately a problem; it had just become unnecessary. Unnecessary code you’re still running, maintaining, and depending on is a cost easy to overlook because nothing is visibly broken.</p>
<p>This is precisely where it matters for platform teams. A platform team’s job is to make the engineering organization more effective, and that works best when the platform stays focused on the goals it is there to achieve. Taking on a custom solution to fill a vendor gap is good technical debt. It’s a conscious tradeoff, made at a specific point in time to keep the platform moving. But technical debt has a repayment point, and when a vendor ships their own solution to something you’ve been solving yourself, that’s usually the moment it arrives. Keeping the custom solution running past the point where it is needed is when good debt turns into bad debt, leaving you paying maintenance overhead and dealing with complexity that no longer needs to exist. The best platform teams recognize that moment and act on it. Let the vendor carry the maintenance burden, absorb the edge cases, and handle the support. This keeps your platform thin, lets you remove complexity, and frees your team to focus on what only you can build.</p>
<p>But none of this is possible if you don’t know what your vendors are shipping. The repayment moment only arrives if you recognize it, and that’s the part that’s easy to miss because keeping up with every tool in your stack, across every channel each vendor uses, can feel like a job in itself.</p>
<h2 id="staying-connected-without-it-becoming-a-job">Staying connected without it becoming a job</h2>
<p>You can’t spend your life reading vendor changelogs. That would be a full-time job, and you already have one. But there’s a meaningful middle ground between obsessively monitoring every release and being completely unaware, and most of the time, good vendors are trying to meet you there. At the end of the day, vendors want you to know about new features and functionality, and start using their solutions to solve the problems you have.</p>
<p>At Octopus, we try to surface what we’re shipping across a range of channels, and the intention is that at least one fits how you already work and where you prefer to consume content. Some examples of where we do this are:</p>
<ul>
<li><a href="https://octopus.com/lp/newsletter-signup">Monthly newsletter</a></li>
<li><a href="https://octopus.com/blog">Blog</a></li>
<li><a href="https://octopus.com/whatsnew">What’s New feed</a></li>
<li>In-app notifications</li>
<li><a href="https://octopus.com/webinars">Webinars</a></li>
<li><a href="https://www.youtube.com/@OctopusDeploy">Deploy on Friday</a></li>
<li><a href="https://twitter.com/OctopusDeploy">Social media</a></li>
<li><a href="https://www.youtube.com/@OctopusDeploy">YouTube</a></li>
<li><a href="https://octopus.com/slack">Community Slack</a></li>
<li><a href="https://octopus.com/roadmap">Roadmap</a></li>
<li><a href="https://octopus.com/news">Press room</a></li>
<li><a href="https://octopus.com/shipped">Shipped (yearly online event)</a></li>
</ul>
<p>Most vendors are making a similar effort. The bar for staying loosely connected is lower than it probably feels.</p>
<p>One approach I’ve found genuinely useful is using AI to do the monitoring for me. There are a few ways to tackle this, but I’ve been using skills in Claude for this use case. You can get Claude to help you build a skill, using the built-in skill builder by saying something like “Can you help me build a skill?”. From there, you can provide context of what you’re interested in, and provide as many resources from the vendor as you can. I specifically note that I’m usually not interested in non-product-related posts (e.g., from social media feeds), and that any duplicate items found across multiple sources should be consolidated.</p>
<p>Your mileage will vary depending on how the vendor provides updates and how consumable those updates are for something like a large language model. Vendors who publish structured, well-maintained feeds like a dedicated changelog page, a What’s New feed, and versioned release notes tend to work much better as sources than those whose updates are scattered across social media posts and marketing announcements. But within about 30 seconds, I can say “grab me the last month’s updates from Octopus Deploy”, and Claude uses the skill to build a response in the format I’ve specified, that I can easily scroll through. Claude is also pretty good at linking to the source if I want to explore something further.</p>
<h2 id="keeping-pace">Keeping pace</h2>
<p>When a vendor ships a solution to something you’ve already solved yourself, moving to it is usually the right call. They’ve absorbed the edge cases, they own maintenance going forward, they’ve researched the feature’s requirements and may build something you didn’t already have, and you get to decommission something. Your team can focus on building things of value and importance to your organization, and you can push complexity down to vendors, creating a thinner platform that floats on top of the capabilities they provide.</p>
<p>My friend Sam was paying attention to what the vendors he uses are doing, saw the AWS announcement, and was able to decommission a custom solution in favor of AWS’s built-in solution. It’s a small habit - staying loosely connected to what the tools you depend on are shipping - but it’s what put him in a position to act when the moment arrived.</p>
<p>When did you last look at what your vendors have been shipping? It’s probably worth 20 minutes to find out.</p>
<p>Happy deployments!</p>]]></content>
    </entry>
    <entry>
      <title>GitOps with Octopus and Argo CD</title>
      <link href="https://octopus.com/blog/argocd-and-octopus" />
      <id>https://octopus.com/blog/argocd-and-octopus</id>
      <published>2026-05-14T00:00:00.000Z</published>
      <updated>2026-05-14T00:00:00.000Z</updated>
      <summary>Supercharge your Argo CD installation with Octopus in order to get the complete Continuous Delivery experience.</summary>
      <author>
        <name>Kostis Kapelonis, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>Argo CD has become the de facto standard for GitOps deployments. It is currently the leading GitOps solution with <a href="https://www.cncf.io/announcements/2025/07/24/cncf-end-user-survey-finds-argo-cd-as-majority-adopted-gitops-solution-for-kubernetes/">60% penetration in the Kubernetes ecosystem</a>. Teams that choose Argo CD also love it, giving it an NPS score of 79 (for comparison, the industry average is 36).</p>
<p>Octopus Deploy has built-in integration with Argo CD. If you want to understand the technical details and setup instructions, feel free to take a look at <a href="https://octopus.com/blog/argo-cd-in-octopus">the announcement</a> and <a href="https://octopus.com/docs/argo-cd">the documentation</a>.</p>
<p>In this post, we want to focus on the “why” of the integration rather than the “how”. We have talked with several customers about the Octopus/Argo CD collaboration, and most discussions always start with similar questions:</p>
<ul>
<li>Why do I need Octopus if I already deploy with Argo CD?</li>
<li>How does Octopus change the Argo CD experience?</li>
<li>Does Octopus improve how GitOps deployments work with Argo CD?</li>
<li>Is GitOps with Octopus better than GitOps with Argo CD?</li>
</ul>
<p>If you have already adopted Argo CD and are wondering about the value of Octopus and how it supercharges your GitOps deployments, read on!</p>
<h2 id="octopus-and-argo-cd-work-together">Octopus and Argo CD work together</h2>
<p>Octopus does not change the excellent GitOps engine that Argo CD already offers. Instead, it allows you to scale Argo CD to Enterprise organizations that have special requirements regarding security, standardization, observability, etc.</p>
<figure><p><img src="/blog/i/x/argocd-and-octopus/argocd-enhanced-with-octopus.png" alt="Argo CD and Octopus for Full Continuous Delivery" loading="lazy" srcset="/blog/i/600/argocd-and-octopus/argocd-enhanced-with-octopus.webp 600w, /blog/i/1000/argocd-and-octopus/argocd-enhanced-with-octopus.webp 1000w, /blog/i/2000/argocd-and-octopus/argocd-enhanced-with-octopus.webp 2000w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="4804" height="3100"></p></figure>
<p>The two tools work together to offer you a complete solution that is appropriate for Enterprise adoption of GitOps best practices.</p>
<h2 id="gitops-standardization-across-your-teams">GitOps standardization across your teams</h2>
<p>Adopting Argo CD on the first team is an easy task. The problems start when Argo CD adoption grows beyond the initial phase within an organization. Can you guarantee that the second, third, fourth, and Nth teams are adopting Argo CD the same way?</p>
<p>Argo CD is a very flexible tool that can be used in multiple ways. This is a double-edged sword. On one hand, it means that Argo CD can cover many use cases. On the other hand, it also means that every team can deviate from the “approved” or “blessed” way if there are no guardrails in place.</p>
<p>The most common pattern we see is with Git commits. Argo CD is great for taking your Git changes and deploying them to a Kubernetes cluster.</p>
<figure><p><img src="/blog/i/x/argocd-and-octopus/argocd-scope.png" alt="Argo CD Git Scope" loading="lazy" srcset="/blog/i/600/argocd-and-octopus/argocd-scope.webp 600w, /blog/i/1000/argocd-and-octopus/argocd-scope.webp 1000w, /blog/i/2000/argocd-and-octopus/argocd-scope.webp 2000w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="6000" height="2440"></p></figure>
<p>This is a well-understood process and the foundation for GitOps deployments. The hidden question here is who updates the Git files? How are they updated? And when? Argo CD doesn’t really care about how a change found its way into Git. But if you are working in an organization that cares about standardization and auditing, you must know that all your teams deploy the same way.</p>
<p>Several times we talk with customers who say they have “standard” GitOps deployments and in reality, they do something like this:</p>
<figure><p><img src="/blog/i/x/argocd-and-octopus/adhoc-git-commit.png" alt="Adhoc Git Commit solution" loading="lazy" srcset="/blog/i/600/argocd-and-octopus/adhoc-git-commit.webp 600w, /blog/i/1000/argocd-and-octopus/adhoc-git-commit.webp 1000w, /blog/i/2000/argocd-and-octopus/adhoc-git-commit.webp 2000w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="6000" height="4136"></p></figure>
<p>This setup is a big problem for organizations that want to scale their Argo CD usage. If every team is doing something else you don’t really have a way to enforce best practices and security processes across your company.</p>
<p>Even if some teams are using the same tool such as GitHub actions or Jenkins, these tools are originally built for Continuous Integration. Abusing them for GitOps deployments presents several challenges that need workarounds and custom solutions in areas like:</p>
<ul>
<li>Manual approvals (especially when coupled with external systems)</li>
<li>Security and access control (who can actually deploy/approve/commit)</li>
<li>Correlation with artifacts and everything else that goes inside a release (auditing and security)</li>
<li>Maintenance overhead and developer onboarding (solved with <a href="https://octopus.com/use-case/platform-hub">Octopus Platform Hub</a>)</li>
</ul>
<p>We talk with several organizations that tell us that they have tried to standardize on using a single solution such as GitHub actions. But even though on paper it seems like they have solved standardization with a custom script in reality:</p>
<ul>
<li>The “solution” is a homegrown script that nobody wants to maintain</li>
<li>Because it is completely custom, it must be constantly updated and adapted to new requirements</li>
<li>It can never scale to the needs of an Enterprise</li>
<li>Day 2 operations are always lengthy and risky as they affect services that are already in production</li>
</ul>
<p>This is where Octopus steps in. Octopus Deploy comes bundled with <strong>standard</strong>, <strong>preconfigured</strong> and <strong>auditable</strong> steps for changing the Git manifests that Argo CD monitors.</p>
<figure><p><img src="/blog/i/x/argocd-and-octopus/standard-git-commit.png" alt="Standard Git commit" loading="lazy" srcset="/blog/i/600/argocd-and-octopus/standard-git-commit.webp 600w, /blog/i/1000/argocd-and-octopus/standard-git-commit.webp 1000w, /blog/i/2000/argocd-and-octopus/standard-git-commit.webp 2000w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="6000" height="4416"></p></figure>
<p>By adding Octopus, you enhance your GitOps deployments by enforcing a consistent approach to Git manifest management across all your teams. If you use Octopus and Argo CD you don’t need to guess what each team is doing to change their Git files. You know exactly what goes into Git, and you also have full visibility into how, when, and by whom all those changes are approved.</p>
<p>A big advantage of Octopus Deploy that is missing in Continuous Integration tools is the concept of <a href="https://octopus.com/docs/releases">the Release</a>. Octopus knows what a release is and has a first class construct for everything that goes inside one. This allows for three significant advantages:</p>
<ol>
<li>Octopus Deploy keeps a detailed record of all the files and artifacts that took part in the deployment for any release, even for past ones. This is important for auditing and security purposes. Standard Continuous Integration tools only know what is happening during the actual run</li>
<li>Octopus Deploy can easily perform rollbacks to previous releases as it has full knowledge on what needs to be done to bring things back to an earlier state. This is critical during incidents when downtime is affected and developers need to go back to the previous version as fast as possible.</li>
<li>With Octopus Deploy you can fully automate all Git commits, essentially preventing developers from making manual commits. If you know that only Octopus Deploy can commit, security controls are audits are now much easier in your organization.</li>
</ol>
<p>This is the common theme for doing GitOps with Octopus. Octopus doesn’t replace the powerful GitOps engine that Argo CD implements. But using both tools together allows you to expand your GitOps practices in an enterprise setting that has different requirements about scale, security, compatibility, and auditing.</p>
<h2 id="application-promotion-between-environments">Application promotion between environments</h2>
<p>At its core, an Argo CD application is a bidirectional link between a Git repository and a Kubernetes cluster. Argo CD continuously monitors the Git repository and deploys the application to the cluster. In a large enterprise, clusters are usually grouped by software release lifecycle (QA/Production/Staging).</p>
<p>Developer teams always want to gradually deploy their applications to a sequence of environments that starts with low-risk clusters and ends with the “production” clusters. For companies under strict regulations (e.g. financial institutions, HLS, Manufacturing controls etc.) this is an even more critical process as developers will be blocked from deploying anything to production unless it has been approved first in lower-risk environments.</p>
<p>Argo CD is excellent for deploying to a single Kubernetes cluster, but it doesn’t have any visibility into what this cluster represents and which “environment” it belongs to. By default, Argo CD will deploy a brand new application to a cluster, and it has no capacity to “promote” an application from a previous environment.</p>
<p>A very popular workaround for this problem is when teams choose a special naming convention for their Argo CD applications:</p>
<figure><p><img src="/blog/i/x/argocd-and-octopus/argocd-products.png" alt="Custom Argo CD names" loading="lazy" srcset="/blog/i/600/argocd-and-octopus/argocd-products.webp 600w, /blog/i/1000/argocd-and-octopus/argocd-products.webp 1000w, /blog/i/2000/argocd-and-octopus/argocd-products.webp 1629w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="1629" height="755"></p></figure>
<p>A human administrator who looks at the picture above will understand that they see a single application called “accounts” which is currently deployed to 3 different environments (qa/prod/staging). This information is only available to human users. As far as Argo CD is concerned, these are 3 different and unrelated applications.</p>
<p>This forces a lot of teams to create custom scripts or tools that handle the promotion of developer applications. This is problematic in so many ways:</p>
<ul>
<li>Different teams might create different scripts, resulting in effort duplication</li>
<li>Promotion scripts are always hard to maintain and hard to adapt to new requirements</li>
<li>Developers are constantly in firefight mode when deployments fail, as they do not understand the differences between environments</li>
<li>Administrators cannot enforce common security policies and company guidelines across their teams</li>
</ul>
<p>Octopus Deploy solves this problem entirely by modeling environments on top of Argo CD applications. In addition, you can model promotion processes directly into Octopus and get full visibility on which state each application is in right now. Understanding what is deployed and where the latest changes are is crucial not only during daily operations but also during incidents. Not knowing what your environments have will result in downtime when incidents happen.</p>
<figure><p><img src="/blog/i/x/argocd-and-octopus/octopus-environments.png" alt="Octopus Argo CD environments" loading="lazy" srcset="/blog/i/600/argocd-and-octopus/octopus-environments.webp 600w, /blog/i/1000/argocd-and-octopus/octopus-environments.webp 1000w, /blog/i/2000/argocd-and-octopus/octopus-environments.webp 1520w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="1520" height="801"></p></figure>
<p>Octopus environments essentially allow developers to promote their applications with less risk, higher confidence, and full visibility.</p>
<p>When an incident happens, all teams always ask the same 2 questions:</p>
<ul>
<li>What was the last change/version that was deployed in the problematic environment?</li>
<li>What is the safe version that we can rollback to, and which environment has guarantees that it is safe to rollback to?</li>
</ul>
<p>These two questions can be answered in seconds with Octopus Deploy using the environments dashboard.</p>
<h2 id="security-and-compliance">Security and compliance</h2>
<p>Deploying an application to production is always the last step in a lengthy process, which, depending on the nature of the company that uses Argo CD might take from a couple of hours to several days. Sending a new feature to production is important, but it is also critical to be able to reason about:</p>
<ul>
<li>How this feature was created</li>
<li>Who implemented it</li>
<li>Who approved it</li>
<li>The security requirements it satisfies</li>
</ul>
<p>Argo CD knows only the last part of the deployment process. It deploys an application into a Kubernetes cluster without any real knowledge of what features are contained in this release or who created it. Again, this forces a lot of teams to create ad-hoc solutions with custom scripts that function as workarounds, implementing essential enterprise requirements such as manual approvals or correlations with the Continuous Integration phase of a software release.</p>
<p>Octopus Deploy already has built-in support for understanding the whole picture of a software release. Octopus Deploy can also interface with external systems and artifact registries in order to create a full snapshot of the release process in a secure and auditable manner.</p>
<figure><p><img src="/blog/i/x/argocd-and-octopus/octopus-process.png" alt="Full deployment by Octopus" loading="lazy" srcset="/blog/i/600/argocd-and-octopus/octopus-process.webp 600w, /blog/i/1000/argocd-and-octopus/octopus-process.webp 1000w, /blog/i/2000/argocd-and-octopus/octopus-process.webp 1296w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="1296" height="749"></p></figure>
<p>Deployment approvals are a critical part of the software lifecycle, especially for companies that operate under strict legislation (e.g. companies with financial or health data). Argo CD does NOT offer an approval mechanism. While it is possible to perform basic approvals with Git, enterprise organizations often want to integrate existing approval mechanisms in their GitOps workflows.</p>
<p>With Argo CD and Octopus Deploy, you get the best of both worlds. Argo CD implements a powerful GitOps engine, while Octopus handles all the requirements for Enterprise deployments.</p>
<p>Further enforcing the standardization aspect, all Octopus workflows can use <a href="https://octopus.com/use-case/platform-hub">Platform Hub templates</a> that result in a secure and controlled workflow process. With Platform Hub your administrators can create and define the approved workflows for how deployment works within an organization. Developers across all teams must then use the approved workflow templates in their own projects. This avoids duplicated effort and custom/ad-hoc deployment processes that might result in downtime or failed security audits.</p>
<h2 id="developer-experience-and-observability">Developer experience and observability</h2>
<p>Helping developers quickly and safely bring their changes to production is vital as this is the only way that new features can reach customers. The time a change needs to reach production is actually one of <a href="https://dora.dev/guides/dora-metrics/">the DORA metrics</a> and has a direct correlation to the performance of an organization.</p>
<p>While Argo CD has a comprehensive User Interface that system administrators love, developers have entirely different needs when it comes to application promotion.</p>
<p>Some examples of the questions that developers need to answer daily are:</p>
<ul>
<li>Which is the last environment that application X was deployed to?</li>
<li>How many versions is “production” behind “staging” for application Y?</li>
<li>What was the last software release that was deployed to environment Z?</li>
</ul>
<p>As we explained already, Argo CD doesn’t model deployment environments. Only Octopus can map business environments (Staging/production, etc) to Argo CD applications. This way, Octopus can quickly answer all these questions in seconds instead of hours.</p>
<p>Octopus is also the best way to show multiple applications from different Argo CD instances in a single dashboard. This way, developers can see aggregated information across the whole organization about what their applications are doing.</p>
<figure><p><img src="/blog/i/x/argocd-and-octopus/unified-gitops-dashboard.png" alt="Single Dashboard" loading="lazy" srcset="/blog/i/600/argocd-and-octopus/unified-gitops-dashboard.webp 600w, /blog/i/1000/argocd-and-octopus/unified-gitops-dashboard.webp 1000w, /blog/i/2000/argocd-and-octopus/unified-gitops-dashboard.webp 2000w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="6000" height="3200"></p></figure>
<p>The Octopus Deploy environment dashboard is a single pane of glass that can</p>
<ul>
<li>Aggregate applications from different Argo CD instances</li>
<li>Show which environments are implemented by the different clusters</li>
<li>Document deployment history on an environment level instead of individual clusters</li>
<li>Help developers promote applications to different environments in a safe way</li>
<li>Give an overview of the live status of applications and their logs</li>
</ul>
<p>Blocking developers from understanding what is deployed where is a crucial problem during incidents. If developers cannot quickly determine which version is deployed in an environment and which version to rollback to, the incident will cause downtime for end users, leading to loss of business and customer dissatisfaction.</p>
<h2 id="conclusion">Conclusion</h2>
<p>By adopting Octopus Deploy for your GitOps process, you can scale Argo CD across a large organization with many clusters/applications/developers. Argo CD still performs the last step of the deployment using its powerful synchronization engine, while Octopus handles all the enterprise requirements that the modern software lifecycle includes, such as approvals, auditing, and compliance.</p>
<p>With Octopus, all developer and operator teams have a single way to deploy to production without any custom scripts or glue code. Octopus standardizes your GitOps workflows by eliminating ad hoc solutions for manifest management that require constant maintenance and are often fragile and complex to update.</p>
<p>At the same time, developers get the full picture of how an application is promoted between different environments and can quickly understand where each application is deployed and what it needs to progress to the next environment. This results in less downtime, especially during incidents when understanding which applications are affecting which environments becomes time-critical.</p>
<p>If you want to know how Octopus can scale your Argo CD installation <a href="https://octopus.com/company/contact">contact us</a> or browse the <a href="https://octopus.com/docs/argo-cd">documentation</a>.</p>
<p>Happy deployments!</p>]]></content>
    </entry>
    <entry>
      <title>Connecting an AWS EKS Argo CD instance to Octopus Cloud</title>
      <link href="https://octopus.com/blog/connecting-aws-eks-argo-cd-to-octopus-cloud" />
      <id>https://octopus.com/blog/connecting-aws-eks-argo-cd-to-octopus-cloud</id>
      <published>2026-05-05T00:00:00.000Z</published>
      <updated>2026-05-05T00:00:00.000Z</updated>
      <summary>Learn how to connect an AWS EKS Argo CD instance to Octopus Deploy.</summary>
      <author>
        <name>Jubril Oyetunji, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>Argo CD has become the go-to tool for adopting GitOps in Kubernetes environments. Here at Octopus Deploy, we are huge fans of it; in fact, we’re <a href="https://insights.linuxfoundation.org/project/argo/contributors?timeRange=past365days&#x26;repos=argoproj-argo-cd,argoproj_argo-cd&#x26;start=2025-04-24&#x26;end=2026-04-24&#x26;widget=organizations-leaderboard">lead maintainers of the Argo project</a>.</p>
<p>In a previous article, we looked at <a href="https://octopus.com/blog/aks-argo-cd-to-self-hosted-octopus">connecting an Argo CD instance running on a Self-Hosted Azure Kubernetes Service (AKS) to Octopus Deploy</a>. While this was neat, AWS still leads the cloud infrastructure market at around 29% as of Q3 2025, according to <a href="https://www.srgresearch.com/articles/cloud-market-share-trends-big-three-together-hold-63-while-oracle-and-the-neoclouds-inch-higher">Synergy Research Group</a>, so if you have been wondering how to replicate that tutorial on EKS, you’re in the right place.</p>
<h2 id="why-connect-argo-cd-on-eks-to-octopus-cloud">Why connect Argo CD on EKS to Octopus Cloud?</h2>
<p>Running Argo CD on EKS gives you GitOps-driven deployments into Kubernetes, but Argo CD alone does not cover the full delivery picture. On the other end of that picture is Octopus, which coordinates the release process around it: approvals, environment progression, runbooks, and a single view across Kubernetes and everything else you ship.</p>
<p>Octopus Cloud customers get a mostly plug-and-play experience because Octopus has already configured the communication rules on its side, and the Helm commands generated by the connection wizard handle the rest (assuming no egress restrictions sit in front of the Argo CD cluster).</p>
<p>In our previous blog, a lot of the steps came about as a result of needing to open up certain ports in order to establish connectivity with your Octopus server; the majority of this goes away when you run in AWS and use Octopus Cloud.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>In order to follow along with this portion of the tutorial, you will need the following tools installed locally.</p>
<ul>
<li><a href="https://developer.hashicorp.com/terraform/install">Terraform</a>, for provisioning ArgoCD and the Argo CD Gateway onto the cluster</li>
<li><a href="https://helm.sh/docs/intro/install/">Helm</a>, for installing the gateway chart</li>
<li><a href="https://kubernetes.io/docs/tasks/tools/">kubectl</a>, for interacting with the cluster</li>
<li><a href="https://argo-cd.readthedocs.io/en/stable/cli_installation/">ArgoCD CLI,</a> for generating an auth token for the Octopus gateway account</li>
<li><a href="https://octopus.com/docs/octopus-rest-api/how-to-create-an-api-key">An Octopus API key</a>, for accessing the Octopus Deploy REST API</li>
</ul>
<h2 id="step-1-provision-a-kubernetes-cluster">Step 1: Provision a Kubernetes cluster</h2>
<p>If you already have a cluster, feel free to skip to the next section. However, if you don’t, the following lines will use the EKS CLI, to create one.</p>
<p>To follow along, run the following commands:</p>
<p>Create a cluster configuration:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26"> cat</span><span style="color:#000000"> &#x3C;&#x3C; </span><span style="color:#000000">'EOF'</span><span style="color:#000000"> > </span><span style="color:#A31515">cluster.yaml</span></span>
<span class="line"><span style="color:#A31515">  apiVersion: eksctl.io/v1alpha5</span></span>
<span class="line"><span style="color:#A31515">  kind: ClusterConfig</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A31515">  metadata:</span></span>
<span class="line"><span style="color:#A31515">    name: basic-cluster</span></span>
<span class="line"><span style="color:#A31515">    region: eu-north-1</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A31515">  nodeGroups:</span></span>
<span class="line"><span style="color:#A31515">    - name: ng-1</span></span>
<span class="line"><span style="color:#A31515">      instanceType: m5.large</span></span>
<span class="line"><span style="color:#A31515">      desiredCapacity: 10</span></span>
<span class="line"><span style="color:#A31515">      volumeSize: 80</span></span>
<span class="line"><span style="color:#A31515">      ssh:</span></span>
<span class="line"><span style="color:#A31515">        allow: true # will use ~/.ssh/id_rsa.pub as the default ssh key</span></span>
<span class="line"><span style="color:#A31515">    - name: ng-2</span></span>
<span class="line"><span style="color:#A31515">      instanceType: m5.large</span></span>
<span class="line"><span style="color:#A31515">      desiredCapacity: 2</span></span>
<span class="line"><span style="color:#A31515">      volumeSize: 100</span></span>
<span class="line"><span style="color:#A31515">      ssh:</span></span>
<span class="line"><span style="color:#A31515">        publicKeyPath: ~/.ssh/id_rsa.pub</span></span>
<span class="line"><span style="color:#A31515">  EOF</span></span></code></pre>
<p>The cluster configuration above will provision two nodes with m5.large as the instance type, feel free to tweak the configuration as you see fit.</p>
<p>Create the cluster by running:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">eksctl</span><span style="color:#A31515"> create</span><span style="color:#A31515"> cluster</span><span style="color:#0000FF"> -f</span><span style="color:#A31515"> cluster.yaml</span><span style="color:#0000FF"> --timeout</span><span style="color:#A31515"> 45m</span></span></code></pre>
<p><strong>NOTE</strong>: The <code>--timeout</code> flag here is to ensure the CLI does not timeout before the cluster is done being provisioned.</p>
<p>Finally, you can obtain the Kubeconfig for the cluster created by running:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">eksctl</span><span style="color:#A31515"> utils</span><span style="color:#A31515"> write-kubeconfig</span><span style="color:#0000FF"> --cluster=basic-cluster</span><span style="color:#0000FF"> --region=eu-north-1</span></span></code></pre>
<h2 id="step-2-install-argocd">Step 2: Install ArgoCD</h2>
<p>The next step is to install ArgoCD. Using the helm chart is the most straightforward means of installing.</p>
<p>Head back to your terminal and run the following command:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">helm</span><span style="color:#A31515"> install</span><span style="color:#A31515"> argocd</span><span style="color:#A31515"> argo-cd</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --repo</span><span style="color:#A31515"> https://argoproj.github.io/argo-helm</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --create-namespace</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --namespace</span><span style="color:#A31515"> argocd</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --wait</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --timeout</span><span style="color:#A31515"> 10m</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --values</span><span style="color:#A31515"> -</span><span style="color:#000000"> &#x3C;&#x3C; </span><span style="color:#000000">'EOF'</span></span>
<span class="line"><span style="color:#A31515">configs:</span></span>
<span class="line"><span style="color:#A31515">  cm:</span></span>
<span class="line"><span style="color:#A31515">    accounts.octopus: apiKey</span></span>
<span class="line"><span style="color:#A31515">  rbac:</span></span>
<span class="line"><span style="color:#A31515">    policy.default: "role:readonly"</span></span>
<span class="line"><span style="color:#A31515">    policy.csv: |</span></span>
<span class="line"><span style="color:#A31515">      g, admin, role:admin</span></span>
<span class="line"><span style="color:#A31515">      p, octopus, applications, get, *, allow</span></span>
<span class="line"><span style="color:#A31515">      p, octopus, applications, sync, *, allow</span></span>
<span class="line"><span style="color:#A31515">      p, octopus, clusters, get, *, allow</span></span>
<span class="line"><span style="color:#A31515">      p, octopus, logs, get, */*, allow</span></span>
<span class="line"><span style="color:#000000">EOF</span></span></code></pre>
<p>The output is similar to:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="text"><code><span class="line"><span>NAME: argocd  </span></span>
<span class="line"><span>LAST DEPLOYED: Fri Apr 15 14:32:08 2026  </span></span>
<span class="line"><span>NAMESPACE: argocd  </span></span>
<span class="line"><span>STATUS: deployed  </span></span>
<span class="line"><span>REVISION: 1  </span></span>
<span class="line"><span>TEST SUITE: None  </span></span>
<span class="line"><span>NOTES:</span></span></code></pre>
<p>The command will install Argo CD into a namespace called <code>argocd</code>, the extra values file tacked on sets up the account Octopus will use to talk to it.</p>
<p><code>accounts.octopus: apiKey</code> creates a dedicated <code>octopus</code> user in Argo CD and enables API key authentication for it, so Octopus has its own identity rather than piggybacking on <code>admin</code>.</p>
<p>The <code>rbac</code> block then scopes what that user can do: <code>policy.default: "role:readonly"</code> makes read-only the baseline for any account without an explicit grant, and the <code>policy.csv</code> lines grant the <code>octopus</code> user only the permissions it needs.</p>
<p>The values above become relevant once we install the Octopus Argo gateway.</p>
<h3 id="what-is-the-octopus-argo-gateway">What is the Octopus Argo gateway?</h3>
<p>Simply put, the Octopus Argo gateway is a component required to establish a secure, TLS-encrypted connection between an Argo CD instance (running in a Kubernetes cluster) and the Octopus Deploy.</p>
<p>In contrast to self-hosted setups where you need to create firewall rules and potentially consider what certs to use to secure the communication, the Octopus Argo gateway handles it all.</p>
<h4 id="generate-an-authentication-token">Generate an authentication token</h4>
<p>In order for the gateway to authenticate with the ArgoCD instance you just deployed, an authentication token is required.  To create one, run the following commands:</p>
<p>Grab the auto-generated admin password:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#001080">ARGOCD_PASSWORD</span><span style="color:#000000">=$(</span><span style="color:#795E26">kubectl</span><span style="color:#A31515"> get</span><span style="color:#A31515"> secret</span><span style="color:#A31515"> argocd-initial-admin-secret</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  -n</span><span style="color:#A31515"> argocd</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  -o</span><span style="color:#A31515"> jsonpath='{.data.password}'</span><span style="color:#000000"> | </span><span style="color:#795E26">base64</span><span style="color:#0000FF"> --decode</span><span style="color:#000000">)</span></span></code></pre>
<p>Port-forward the Argo CD server to <code>localhost:18080</code>:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">kubectl</span><span style="color:#A31515"> port-forward</span><span style="color:#A31515"> svc/argocd-server</span><span style="color:#0000FF"> -n</span><span style="color:#A31515"> argocd</span><span style="color:#A31515"> 18080:443</span><span style="color:#000000"> &#x26;</span></span>
<span class="line"><span style="color:#001080">PF_PID</span><span style="color:#000000">=</span><span style="color:#0000FF">$!</span></span>
<span class="line"><span style="color:#795E26">sleep</span><span style="color:#098658"> 5</span></span></code></pre>
<p>Log in as admin:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">argocd</span><span style="color:#A31515"> login</span><span style="color:#A31515"> localhost:18080</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --username</span><span style="color:#A31515"> admin</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --password</span><span style="color:#A31515"> "</span><span style="color:#001080">$ARGOCD_PASSWORD</span><span style="color:#A31515">"</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --insecure</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --grpc-web</span></span></code></pre>
<p>Mint a token for the octopus account:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">argocd</span><span style="color:#A31515"> account</span><span style="color:#A31515"> generate-token</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --account</span><span style="color:#A31515"> octopus</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --insecure</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --grpc-web</span></span></code></pre>
<p>Be sure to copy and save the token</p>
<p>Clean up the port-forward:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">kill</span><span style="color:#001080"> $PF_PID</span></span></code></pre>
<h2 id="step-3-link-your-cluster">Step 3: Link your cluster</h2>
<p>From here on out, installing the Octopus Argo gateway is only a few clicks.</p>
<p>Within the sidebar on the left side of your Octopus dashboard, click on the tab <strong>Argo CD Instances</strong>.</p>
<figure>
<p><img src="/blog/i/x/connecting-aws-eks-argo-cd-to-octopus-cloud/argo-instances-menu.jpg" alt="Octopus dashboard highlighting the Argo CD instances menu option" srcset="/blog/i/600/connecting-aws-eks-argo-cd-to-octopus-cloud/argo-instances-menu.webp 600w, /blog/i/1000/connecting-aws-eks-argo-cd-to-octopus-cloud/argo-instances-menu.webp 1000w, /blog/i/2000/connecting-aws-eks-argo-cd-to-octopus-cloud/argo-instances-menu.webp 1994w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="1994" height="1118"></p>
<figcaption>
Octopus dashboard displaying the Argo CD Instances sidebar menu option
</figcaption>
</figure>
<p>Within the Argo CD instance tab click on <strong>Add Argo Instance</strong>.</p>
<figure>
<p><img src="/blog/i/x/connecting-aws-eks-argo-cd-to-octopus-cloud/add-argo-instance.jpg" alt="Screenshot of the Argo Instance button at the top of the Argo CD instances screen" srcset="/blog/i/600/connecting-aws-eks-argo-cd-to-octopus-cloud/add-argo-instance.webp 600w, /blog/i/1000/connecting-aws-eks-argo-cd-to-octopus-cloud/add-argo-instance.webp 1000w, /blog/i/2000/connecting-aws-eks-argo-cd-to-octopus-cloud/add-argo-instance.webp 1993w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="1993" height="284"></p>
<figcaption>
The Argo Instance button
</figcaption>
</figure>
<p>Up next, you should be greeted with the following menu.</p>
<figure>
<p><img src="/blog/i/x/connecting-aws-eks-argo-cd-to-octopus-cloud/register-argo-cd-instance.png" alt="The Argo CD instance registration form" srcset="/blog/i/600/connecting-aws-eks-argo-cd-to-octopus-cloud/register-argo-cd-instance.webp 600w, /blog/i/1000/connecting-aws-eks-argo-cd-to-octopus-cloud/register-argo-cd-instance.webp 923w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="923" height="1017"></p>
<figcaption>
The Argo CD instance registration form
</figcaption>
</figure>
<p>Enter a name for this cluster’s connection, and if you wish you can select an environment for the ArgoCD instance. Leave the DNS name as the default unless you modified it during the Helm install.</p>
<p>Paste in the authentication token you generated earlier and hit <strong>next</strong>.</p>
<figure>
<p><img src="/blog/i/x/connecting-aws-eks-argo-cd-to-octopus-cloud/configure-project-with-argo.jpg" alt="Configuring a project to deploy with Argo CD" srcset="/blog/i/600/connecting-aws-eks-argo-cd-to-octopus-cloud/configure-project-with-argo.webp 600w, /blog/i/1000/connecting-aws-eks-argo-cd-to-octopus-cloud/configure-project-with-argo.webp 1000w, /blog/i/2000/connecting-aws-eks-argo-cd-to-octopus-cloud/configure-project-with-argo.webp 1575w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="1575" height="934"></p>
<figcaption>
Configuring a project to deploy with Argo CD
</figcaption>
</figure>
<p>Select which project you would like to connect your Argo instance to and hit <strong>Configure Project.</strong></p>
<p>Right after the configuration wizard, it will return a Helm install command which looks something like this.</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#795E26">helm</span><span style="color:#A31515"> install</span><span style="color:#0000FF"> --atomic</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --create-namespace</span><span style="color:#0000FF"> --namespace</span><span style="color:#A31515"> octo-argo-gateway-eks-dev</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --version</span><span style="color:#A31515"> "*.*"</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --set</span><span style="color:#A31515"> registration.octopus.name="EKS-dev"</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --set</span><span style="color:#A31515"> registration.octopus.serverApiUrl="https://&#x3C;your-octopus-instance>.octopus.app/"</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --set</span><span style="color:#A31515"> registration.octopus.serverAccessToken="&#x3C;your-octopus-server-access-token>"</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --set</span><span style="color:#A31515"> registration.octopus.environments="{development}"</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --set</span><span style="color:#A31515"> registration.octopus.spaceId="Spaces-1"</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --set</span><span style="color:#A31515"> gateway.octopus.serverGrpcUrl="grpc://&#x3C;your-octopus-instance>.octopus.app:8443"</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --set</span><span style="color:#A31515"> gateway.argocd.serverGrpcUrl="grpc://argocd-server.argocd.svc.cluster.local"</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --set</span><span style="color:#A31515"> gateway.argocd.insecure="true"</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --set</span><span style="color:#A31515"> gateway.argocd.plaintext="false"</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#0000FF">  --set</span><span style="color:#A31515"> gateway.argocd.authenticationToken="&#x3C;your-argocd-token>"</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#A31515">  eks-dev</span><span style="color:#EE0000"> \</span></span>
<span class="line"><span style="color:#A31515">  oci://registry-1.docker.io/octopusdeploy/octopus-argocd-gateway-chart</span></span></code></pre>
<p>Paste the command  in your terminal and In a couple of seconds your cluster should be connected!</p>
<figure>
<p><img src="/blog/i/x/connecting-aws-eks-argo-cd-to-octopus-cloud/confirmation.png" alt="Confirmation of a connected cluster in the Argo CD Instance tab" srcset="/blog/i/600/connecting-aws-eks-argo-cd-to-octopus-cloud/confirmation.webp 600w, /blog/i/1000/connecting-aws-eks-argo-cd-to-octopus-cloud/confirmation.webp 1000w, /blog/i/2000/connecting-aws-eks-argo-cd-to-octopus-cloud/confirmation.webp 1999w" sizes="(min-width: 1680px) 1000px, (min-width: 940px) calc(71.81vw - 192px), calc(100vw - 32px)" class="resp-img" width="1999" height="787"></p>
<figcaption>
Confirmation of a connected cluster in the Argo CD Instance tab
</figcaption>
</figure>
<h2 id="when-do-i-pick-octopus-cloud-over-self-hosted">When do I pick Octopus Cloud over self-hosted?</h2>
<p>If you caught <a href="https://octopus.com/blog/aks-argo-cd-to-self-hosted-octopus">our last blog on connecting an AKS cluster to a self-hosted Octopus server</a>, you might be wondering which path fits your setup.</p>
<p>Octopus Cloud makes sense when you want the connection to just work. The wizard generates the Helm command, you paste a token, and you are done. If your team does not have a strong reason to run Octopus on your own infrastructure, this is a reasonable path.</p>
<p>Self-hosted Octopus makes sense when the answer to “can the server live in our VPC?” is “it has to.” which usually comes down to data residency rules, existing on-premises infrastructure you need to keep using, or security policies that will not let a third party hold your deployment control plane.</p>
<p>You pay for that control with the networking work in Shawn’s post: opening inbound ports, handling internal DNS, dealing with certs signed by your own CA, and configuring load balancers for gRPC. None of it is hard, but it is work.</p>
<h2 id="wherever-your-clusters-live-octopus-fits">Wherever your clusters live, Octopus fits</h2>
<p>In this post, we walked through connecting an EKS cluster to Octopus Cloud.</p>
<p>We also looked at some of the tradeoffs between doing this in a self-hosted environment versus the cloud.</p>
<p>Whichever path fits your constraints, the outcome is the same. Argo CD keeps doing GitOps. Octopus handles approvals, environment progression, runbooks, and everything outside Kubernetes that you still need to ship.</p>
<p>Spin up a <a href="https://octopus.com/start">free Octopus Cloud instance</a> and try the Argo CD integration for yourself, or <a href="https://octopus.com/lp/schedule-a-demo">book a demo</a> if you want a walkthrough.</p>
<p>Happy deployments!</p>]]></content>
    </entry>
    <entry>
      <title>Financial industry compliance, the DevOps way</title>
      <link href="https://octopus.com/blog/financial-industry-compliance" />
      <id>https://octopus.com/blog/financial-industry-compliance</id>
      <published>2025-04-30T00:00:00.000Z</published>
      <updated>2026-05-05T00:00:00.000Z</updated>
      <summary>Find out how to apply DevOps thinking to financial industry regulation and compliance.</summary>
      <author>
        <name>Steve Fenton, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>If you work in the <a href="https://octopus.com/industry/financial-services">financial industry</a>, you know there can sometimes be a high burden of regulation. This is understandable when you consider the risk of financial loss and how it can impact people’s lives. But if you don’t take the right approach to compliance, it can become a time and energy leak that threatens your ability to compete in the market.</p>
<p>The best compliance teams look for ways to protect the organization and its customers from risk without overburdening teams with bureaucratic procedures. You want compliance set to maximum, and the ability to deliver high-quality, valuable software set to maximum. You don’t want some compromise between the two.</p>
<h2 id="good-compliance-sounds-like-devops">Good compliance sounds like DevOps</h2>
<p>If you know DevOps, this will sound familiar. Traditional managers saw software delivery as a compromise between throughput (creating and improving software features) and stability (keeping everything working). They measured developers on throughput and ops teams on stability, then wondered why there was so much conflict between them.</p>
<p>Just as the solution to the software delivery problem was to align both teams to the shared goal of throughput and stability, you must do the same with other desirable system qualities, like security and compliance.</p>
<p>Rather than calling this DevSecOps or DevSecCompOps, we can call it DevOps because everything that relates to software fitness is part of DevOps.</p>
<p>The non-DevOps way is for stability to be an afterthought and compliance to be an ominous late reaction to an upcoming audit. The DevOps way is to make these part of the daily work, automate as much as possible, and deliver the outcome of compliance instead of the frantic output of audit theater.</p>
<p>It’s about being safe and secure, not looking safe and secure.</p>
<h2 id="compliance-automation">Compliance automation</h2>
<p>You can approach compliance by copying what other organizations are doing. Most organizations have a spreadsheet with obliquely referenced requirements that developers are regularly asked to update and evidence. Who doesn’t love an ORG-05.15-ACCESS-CONTROL line item in a spreadsheet?</p>
<p>However, the best compliance teams I’ve worked with took a different approach. They spent more time educating developers like me about the reasons for these requirements so we could find ways to satisfy them, collect evidence, and keep customers safe.</p>
<p>This meant we considered compliance needs when writing features, selecting tools, or changing our process. When we looked at tools for builds, deployments, and monitoring, ease of meeting compliance needs and automation of evidence collection were part of the buying decision.</p>
<p>This isn’t just about making audits easy; it’s about making compliance easy, and it’s about making the desired outcomes of compliance more likely. Your toolchain can do much of the heavy lifting, leaving you to show and tell during an audit while listening out for opportunities to make things even better.</p>
<h2 id="how-octopus-supports-easy-compliance">How Octopus supports easy compliance</h2>
<p>Octopus has many features that ease your compliance burden and increase safety, from enterprise-grade access controls and audit trails to innovative automation. Let’s take a quick tour of some crucial compliance features:</p>
<ul>
<li>Role-based access control</li>
<li>Extensive audit trails</li>
<li>IT service management integrations</li>
<li>Platform Hub policies and templates</li>
<li>Octolint</li>
<li>Runbooks</li>
</ul>
<h3 id="role-based-access-control">Role-based access control</h3>
<p>Octopus Deploy has fine-grained role-based access controls (RBAC) that integrate with single sign-on (SSO) providers. You can define roles and permissions to control who can change deployment processes, runbooks, and other configurations. You can also define who can access the automation for different projects and environments.</p>
<p>You can use RBAC to prevent unauthorized changes and enforce segregation of duties. You can also safely create self-service actions, like letting testers deploy to the test environment or clear a web cache to speed up their testing without giving them more extensive permissions.</p>
<figure>
<p><img src="/blog/img/financial-industry-compliance/role-based-access.png" alt="A list of built-in user roles."></p>
</figure>
<h3 id="extensive-audit-trail">Extensive audit trail</h3>
<p>Octopus automatically captures a detailed audit log for all significant system events. This includes deployments, configuration changes, user access events, and more. Octopus records every action that changes a state, including who initiated it.</p>
<p>These tamper-resistant audit logs let you demonstrate the changes made to applications or infrastructure. When you need to delve into the audit trail, it’s easy to filter actions by user or by a specific area in Octopus, like spaces, projects, environments, event types, and more.</p>
<figure>
<p><img src="/blog/img/financial-industry-compliance/audit-trail.png" alt="The audit trail screen shows recent changes, deployments, and runbook runs and has filters to easily find what you need."></p>
</figure>
<h3 id="it-service-management-integrations">IT service management integrations</h3>
<p>Our integrations with ServiceNow and Jira Service Management remove manual hand-offs between Octopus and your IT service management (ITSM) platform. This makes it easy to ensure changes have the appropriate approval before progressing.</p>
<p>The integrations automatically create change requests associated with a deployment. You can block deployments until the change gets approved. This increases compliance, as no unapproved changes get deployed to production—but it does so without creating toil for the developers.</p>
<figure>
<p><img src="/blog/img/financial-industry-compliance/approval-flow.png" alt="When a protected environment is selected, the integration with ITSM tools is triggered and a change approval is created and linked. The deployment continues when the change is approved in the ITSM tool."></p>
</figure>
<h3 id="platform-hub">Platform Hub</h3>
<p>Platform Hub lets you define policies that deployments must meet before they can proceed. When a deployment fails a check, Octopus blocks it and surfaces an explanation with links to your standards or internal guidance so that teams can fix the issue without a slow back-and-forth with compliance.</p>
<p>Process and project templates give teams a consistent starting point for pipelines. You configure sound defaults once and reuse them, which makes it easier for teams to meet policy requirements without reinventing their setup for every project.</p>
<figure>
<p><img src="/blog/img/financial-industry-compliance/policies.png" alt="An example warning from a non-compliant deployment."></p>
</figure>
<h3 id="octolint">Octolint</h3>
<p>Octolint is a tool that scans your Octopus instance and recommends improvements to your setup. It detects potential issues such as perpetual API keys, shared Git user names, and unused deployment targets.</p>
<p>Instead of manually auditing for good practices, Octolint can automatically scan and report on items that need your attention.</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="text"><code><span class="line"><span>[OctoLintDefaultProjectGroupChildCount] The default project group contains 79 projects. You may want to organize these projects into additional project groups.</span></span>
<span class="line"><span>[OctoLintEmptyProject] The following projects have no runbooks and no deployment process: Azure Octopus test</span></span>
<span class="line"><span>[OctoLintTooManySteps] The following projects have 20 or more steps: K8s Yaml Import 2</span></span></code></pre>
<h3 id="runbooks">Runbooks</h3>
<p>Financial institutions need good plans for both daily operations and emergencies. Octopus Runbooks help with this. You can turn routine and emergency operations tasks into reusable automations for things like:</p>
<ul>
<li>Database maintenance</li>
<li>Restarting systems</li>
<li>Applying urgent fixes</li>
<li>Recovering backed-up data</li>
</ul>
<p>Automated runbooks are safer and more reliable, reducing the need for broad distribution of elevated access. Runbooks in Octopus also log changes and runs to the standard Octopus audit trail. You can also use runbooks to automate compliance tasks like recovery testing, audit reporting, or probing firewall ports.</p>
<figure>
<p><img src="/blog/img/financial-industry-compliance/operations-overview.png" alt="A list of runbooks that have been triggered and whether they succeeded."></p>
</figure>
<h2 id="octopus-loves-compliance">Octopus loves compliance</h2>
<p>Octopus has many features that reduce the compliance burden and help you build security, privacy, and compliance into your DevOps process. As an organization, Octopus also maintains compliance with ISO 27001:2013, SOC 2 Type II, and SOC 3 with regular third-party audits and technical assessments for security, safety, and privacy. You can find out more about this in our <a href="https://trust.octopus.com/">trust center</a>.</p>
<p>Tools that reduce the compliance burden are crucial to organizations in the financial industry or other regulated and safety-critical environments. Your toolchain can help maintain innovation and compliance just like it brings you throughput and stability.</p>
<p>Happy deployments!</p>]]></content>
    </entry>
    <entry>
      <title>Continuous Delivery Office Hours Ep.4: Mono, micro, mesco</title>
      <link href="https://octopus.com/blog/continuous-delivery-office-hours-e4" />
      <id>https://octopus.com/blog/continuous-delivery-office-hours-e4</id>
      <published>2026-04-30T00:00:00.000Z</published>
      <updated>2026-04-30T00:00:00.000Z</updated>
      <summary>Explore the trade-offs between majestic monoliths, microservices, and mescoservices in this episode of Continuous Delivery Office Hours, including how team structures influence architectural choices.</summary>
      <author>
        <name>Steve Fenton, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>After tackling the question of <a href="/blog/continuous-delivery-office-hours-e3">which branching strategy is best</a>, we turn our focus to architecture in this Continuous Delivery Office Hours episode. In particular, we discuss how big your building blocks should be and whether you should opt for a majestic monolith, granular microservices, or something in between.</p>
<p>Traditional monoliths are often described as a <em>big ball of mud</em> or <em>spaghetti code</em> due to the complicated mess of dependencies. When all the code is co-located, it’s common to find a tangled web of paths, with interfaces and facades skipped in favor of direct access to low-level functions and classes. This kind of code is hard to maintain.</p>
<p>Majestic monoliths are well-structured and loosely coupled, with carefully designed dependencies that are sometimes enforced by architectural tools. By making things modular, you get the benefits often associated with microservices, but without the difficulty of finding the code or the runtime complexity of distributed systems.</p>
<p>Microservices go a step further by physically separating parts of the system into independently deployable units. In addition to the architectural benefits, you can get faster build and test run times because you only build and test each service when it changes, which involves much less code. The trade-off is that your codebase becomes more complex, and you have to deal with versioning and issue pinpointing across all the deployed services.</p>
<p>So, how do you choose between these approaches?</p>
<h2 id="watch-the-episode">Watch the episode</h2>
<p>You can watch the episode below, or read on to find some of the key discussion points.</p>
<p><a href="https://www.youtube.com/watch?v=CuFg-Cjib5Q">Watch Continuous Delivery Office Hours Ep.4</a></p>
<h2 id="architectural-decision-factors">Architectural decision factors</h2>
<p>Many factors influence the decision between monoliths, microservices, and hybrid approaches. One of the most crucial is how your teams are structured and how much autonomy they need. There are different ways to design <a href="https://octopus.com/devops/culture/team-structures/">team structures</a>, like Team Topologies, that make your teams and architecture part of the same conversation.</p>
<p>This is consistent with the <em>inverse Conway maneuver</em>, in which you structure your teams to match your target architecture. This works because Conway’s Law states that organizations design systems that mirror their communication structures. Working at the team level is more effective than creating an architectural diagram, as the structure will naturally yield the design you want.</p>
<p>Having a mismatch between the number of teams and the number of components can add complexity, either because too many teams are working on a single module, or you have created so many modules that there aren’t enough teams to own all the maintenance tasks associated with them. A sensible default is to match the number of teams to the number of components, ensuring sufficient autonomy for each team to work independently without overburdening them with excessive complexity.</p>
<p>The matching of service and team design sits between monolith and microservices, and might be described as “mescoservices” to highlight the balance they strike between the macro level of monoliths and the fine-grained level of microservices.</p>
<p>Another factor that can influence your decision is your deployment model. A managed software-as-a-service offering can use microservices without imposing complexity on its customers. In contrast, a self-hosted software offering may benefit from easier installation, troubleshooting, and upgrades if it uses a monolithic architecture.</p>
<h2 id="common-microservice-failure-modes">Common microservice failure modes</h2>
<p>There are several common failure modes to watch out for when moving to a microservice architecture.</p>
<p>Distributed monoliths look like microservices, except they have dependencies that mean you have to deploy services in a specific order. If you can’t deploy independently, you don’t have microservices. Similarly, you often find microservices at the application layer, but all sharing the same database. This prevents independent scaling of high-load services and can cause services to access data directly rather than through the appropriate service.</p>
<p>When you deploy large numbers of microservices, you may need to introduce rules about which services can communicate. Allowing any service to call any other service means managing a complex web of dependencies, and it starts to look a lot like the proverbial <em>big ball of mud</em>. This is often the result of splitting a monolith into microservices, as the old design tends to persist.</p>
<p>Microservices should bring a high level of team independence, allowing teams to make decisions about their own services, persistence, and deployment schedules.</p>
<h2 id="pulling-microservices-together">Pulling microservices together</h2>
<p>To make microservices successful, you need to establish data ownership and decide what level of data duplication is acceptable to support the business goals. For example, there’s a temptation to store a single address for a customer, but it may be a better idea to store the address immutably against events, like orders. Even if the customer moves house, the address for a past order remains the same.</p>
<p>Domain-driven design provides a method and a language for making decisions across service boundaries. A principal engineer or architect would usually set standards for service boundaries, contracts, and versioning to help keep services loosely coupled and independently deployable.</p>
<h2 id="fitness-test">Fitness test</h2>
<p>The best test of your service-based architecture is independent deployability. If each team can deploy its services without cross-team coordination, then your architecture is working. For majestic monoliths, the test is similar; teams should be able to work independently on modules they own. While deployments will be coupled through a single build and deployment pipeline, they should be able to make and commit changes independently.</p>
<p>Happy deployments!</p>
<div class="hint"><p>Continuous Delivery Office Hours is a series of conversations about software delivery, with Tony Kelly, Bob Walker, and Steve Fenton.</p><p>You can find more episodes on <a href="https://www.youtube.com/playlist?list=PLAGskdGvlaw3CrxkUOAMmiy928lr5D4oh">YouTube</a>, <a href="https://podcasts.apple.com/us/podcast/continuous-delivery-office-hours/id1872101651">Apple Podcasts</a>, and <a href="https://pca.st/hwjaox59">Pocket Casts</a>.</p></div>]]></content>
    </entry>
    <entry>
      <title>Safely creating projects with the AI Assistant</title>
      <link href="https://octopus.com/blog/safely-creating-projects-with-ai-assistant" />
      <id>https://octopus.com/blog/safely-creating-projects-with-ai-assistant</id>
      <published>2026-04-28T00:00:00.000Z</published>
      <updated>2026-04-28T00:00:00.000Z</updated>
      <summary>Learn how to create projects with the Octopus AI Assistant and how to roll back changes if needed.</summary>
      <author>
        <name>Jubril Oyetunji, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>AI in DevOps faces an interesting problem.</p>
<p>According to <a href="https://octopus.com/news/ai-pulse-report">Octopus Deploy’s AI Pulse report</a>, 44% of developers say they’re frustrated with AI outputs that are “almost right, but not quite.”</p>
<p>You’ve likely experienced it yourself: a large language model generates a deployment config missing a critical variable, or suggests a step that works in staging but breaks in production. Which is why many engineers will use AI to whip up a script. Fewer will let it configure a production pipeline unsupervised.</p>
<p>So far in the Easy Mode series, we’ve walked through how to use the Octopus AI Assistant for specific, bounded tasks, from adding manual interventions to setting environment-scoped variables.</p>
<p>Each post focused on a task where you could verify the result before it hit production. This post continues that pattern. We’ll cover how to create projects and what to do if you need to rollback a change you are not satisfied with.</p>
<h2 id="what-the-ai-assistant-does-and-what-were-building">What the AI Assistant does (and what we’re building)</h2>
<p>If this is your first easy mode article, it is worth explaining what the Octopus AI assistant is and exactly what we will be building.</p>
<p>The <a href="https://chromewebstore.google.com/detail/octopus-ai-assistant/agfpjjibnieiihjoehophlbamcifdfha?pli=1">Octopus AI assistant</a> is a Chrome extension that integrates AI directly into Octopus Deploy. It can answer questions about your instance, help you build projects, and guide you through configuration, all without leaving the browser tab you are already in.</p>
<p>A common question users often have is “Does Octopus use my data for training?” to get that out of the way, the extension does NOT train AI models on your data.</p>
<h2 id="demo-creating-a-project-with-the-ai-assistant">Demo: Creating a project with the AI Assistant</h2>
<p>In this walkthrough, we will create a <a href="https://octopus.com/docs/projects">project</a> using the Octopus AI assistant and simulate responding to a request to revert a change.</p>
<h3 id="prerequisites">Prerequisites</h3>
<p>In order to follow along with this portion of the tutorial, you will need the following:</p>
<ul>
<li><strong>An Octopus Cloud account:</strong> If you don’t have one, you can sign up for a free trial.</li>
<li><strong>The Octopus AI Assistant Chrome extension:</strong> You can install it from the <a href="https://chromewebstore.google.com/detail/octopus-ai-assistant/agfpjjibnieiihjoehophlbamcifdfha">Chrome Web Store.</a></li>
</ul>
<p>Upon installation, you should see a new icon in the bottom right.</p>
<p><video src="/blog/img/safely-creating-projects-with-ai-assistant/octopus-ai-assistant.mp4" width="1720" height="1080" alt="Visual of the Octopus AI Assistant after Chrome extension installation" controls muted></video></p>
<h3 id="create-a-project">Create a project</h3>
<p>With the extension installed, the next step is to create a project.</p>
<p>Paste the following prompt into the Octopus AI Assistant and run it:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="text"><code><span class="line"><span>Create a Script project called "15. Git backed Project".</span></span></code></pre>
<p><video src="/blog/img/safely-creating-projects-with-ai-assistant/creating-project-with-ai-assistant.mp4" width="1720" height="1080" alt="Visual of creating and executing a project in Octopus AI assistant" controls muted></video></p>
<p>Upon hitting enter, the Octopus AI assistant will process your request and generate the OpenTofu code required. This is important because using OpenTofu makes your results much more deterministic.</p>
<figure>
<p><img src="/blog/img/safely-creating-projects-with-ai-assistant/generating-opentofu.jpg" alt="Generating the OpenToFu"></p>
<figcaption>Generating the OpenTofu.</figcaption>
</figure>
<p>Many engineers have expressed doubts when using generative AI, as the results tend to vary. Generating the OpenTofu required to fulfill your requests helps the assistant achieve greater accuracy, as there is only one way to create a project via the OpenTofu provider.</p>
<p>Additionally, if you were to do this manually, it would take anywhere from a couple of minutes to a few minutes, depending on how many projects you intend to create and whether you need to modify an existing OpenTofu module.</p>
<h2 id="your-safety-net-version-controlled-projects">Your safety net: Version-controlled projects</h2>
<p>So far, the Octopus AI assistant has created your project. The obvious question is: what if something is wrong?</p>
<p>Thankfully, Octopus Deploy lets you back any project with a Git repository. When you enable this, every change to that project, whether you made it or the assistant did, becomes a Git commit.</p>
<p>To set this up, go to your project settings and connect a Git repository. Once connected, Octopus stores your project configuration in a <code>.octopus</code> folder in that repo. Deployment processes, variables, triggers, all of it lives in version-controlled files.</p>
<p><video src="/blog/img/safely-creating-projects-with-ai-assistant/enabling-version-control.mp4" width="1720" height="1080" alt="Visual of version control being enabled in project settings" controls muted></video></p>
<p>Now look at what happens after the AI assistant creates your project. Open the Git history for the repo. You’ll see the commits the assistant generated, and you can inspect exactly what it configured.</p>
<figure>
<p><img src="/blog/img/safely-creating-projects-with-ai-assistant/committed-files.png" alt="Visual image of the committed files"></p>
<figcaption>Visual image of the committed files.</figcaption>
</figure>
<p>Clicking into any commit gives you the full diff, and if something looks off, you have two options: fix it in the next commit or revert to a known-good state. This is the same workflow you already use for application code.</p>
<figure>
<p><img src="/blog/img/safely-creating-projects-with-ai-assistant/diffs.png" alt="Visual image of the diffs"></p>
<figcaption>Visual image of the diffs.</figcaption>
</figure>
<p>This matters because you can use the assistant with more confidence, because changes are auditable, and because relying on a config-as-code approach means there is only one way to do things.</p>
<h2 id="tips-for-getting-the-most-out-of-ai-assisted-setup">Tips for getting the most out of AI-Assisted setup</h2>
<p>At this stage, if you’re looking to try out an AI-Assisted workflow, here are some quick tips on how to get better returns on your new workflow:</p>
<ul>
<li><strong>Write short, specific prompts:</strong> The more detail you pack into a single prompt, the longer the assistant takes to process it. Keep prompts scoped to a single task.</li>
<li><strong>Include platform and environment details:</strong> The assistant produces better results when you tell it what you’re deploying to. Specify the target (Kubernetes, Azure App Service, AWS ECS), name your environments, and indicate your preferred deployment strategy (rolling, blue/green, canary) if applicable.</li>
<li><strong>Plan to customize after generation:</strong> The assistant handles straightforward project scaffolding well. Complex variable scoping, multi-tenant configurations, and custom scripts are better handled manually after the initial setup. Use the AI to get the structure in place, then fine-tune. Git tracks everything either way.</li>
</ul>
<p>Beyond project creation, the AI Assistant can also help with troubleshooting failed deployments and surfacing best-practice recommendations.</p>
<h2 id="build-fast-ship-safe">Build Fast, Ship Safe</h2>
<p>In this post, we used the Octopus AI Assistant to create a project from a prompt and then backed it with Git via Config as Code.</p>
<p>While the conversation around AI in DevOps evolves, having the ability to use it on your own terms is what makes the Octopus AI assistant a good choice for teams looking to dip their toes in the water.</p>
<p>If you want to try this yourself, <a href="https://chromewebstore.google.com/detail/octopus-ai-assistant/agfpjjibnieiihjoehophlbamcifdfha">install the Octopus AI Assistant Chrome extension</a> and give it a shot.</p>
<p>Happy deployments!</p>]]></content>
    </entry>
    <entry>
      <title>Platforms before portals, build the roads before you buy the GPS</title>
      <link href="https://octopus.com/blog/platforms-before-portals" />
      <id>https://octopus.com/blog/platforms-before-portals</id>
      <published>2026-04-16T00:00:00.000Z</published>
      <updated>2026-04-16T00:00:00.000Z</updated>
      <summary>A lot of the conversation around internal developer platforms gets pulled toward portals. This post argues for a platform-first mindset, and why solid foundations matter more than a slick interface.</summary>
      <author>
        <name>Matthew Allford, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>The acronym IDP does a lot of heavy lifting in platform engineering circles. Ask ten people what it stands for, and you’ll typically get two answers: internal developer <em>platform</em> and internal developer <em>portal</em>. Maybe even a rogue Identity Provider just for fun, too! But seriously, same letters, very different things, and it matters more than it might seem.</p>
<p>A portal is the interface. It’s where a developer logs in and finds everything they need in one place, including services, pipelines, documentation, logging, observability, and ownership. They don’t necessarily need to know which tool sits behind each action, or who to ask for access. It’s a genuinely compelling end state. But it’s just that — an end state.</p>
<p>I see and hear about many teams trying to build the <abbr title="Global Positioning System">GPS</abbr> before the roads exist.</p>
<p>A GPS is an incredible tool. It gets you where you’re going, surfaces the best route, and removes the guesswork. But if the roads aren’t there, or if they’re unpaved, inconsistently maintained, or only connected in some places, the GPS doesn’t fix that. It just navigates you into a field with more confidence, resulting in a poor user experience. The same is true of a portal sitting on top of a platform that isn’t ready to support it.</p>
<p>Portals have real value. But so does sequencing. Get the platform right first, and the portal becomes genuinely powerful. Rush to the portal before the foundations are solid, and you’re surfacing chaos through a clean UI.</p>
<h2 id="when-the-portal-becomes-the-platform-teams-second-job">When the portal becomes the platform team’s second job</h2>
<p>I understand the appeal of a nice, polished developer portal. That single place where teams can go to self-service infrastructure, discover documentation, review application performance, all without filing a ticket or waiting on someone else. They provide a service catalogue, integrations across the tooling used in your platform, and get users to the desired golden paths fast.</p>
<p>One problem is the cost of getting to that point, and teams that don’t consider that cost and prioritize a portal before the underlying platform is ready for it can open themselves up for a world of pain. Portals are a product in their own right and will need to be either procured or built yourself, or a combination of the two. Whichever way you go, it’s going to require the usual care and feeding - engineering time, maintenance, new features and functionality, and one way or another, funding. That’s engineering time that isn’t going toward making the platform itself more reliable, more consistent, or easier to use.</p>
<p>I’ve spoken with Platform Engineers who have been asked to provision a portal for their platform, and go down a rabbit hole of open source solutions and evaluating commercial solutions, only to discover the portal itself is going to take more than a couple of hours a week to maintain, and that’s before you’ve written a single integration. The realization that a portal needs to be managed on top of the Platform itself is daunting to many Platform teams, who are already stretched thin.</p>
<p>A portal then also influences future Platform tooling choices. How hard is it to integrate new tooling with the portal? Does the new tool provide integrations or plugins out of the box, or does the platform team need to build and maintain them themselves?</p>
<p>The organizations that tend to get value from a dedicated portal are those operating at significant scale, with hundreds of developers and dozens of teams, and complex enough that the cost of running a portal becomes a rounding error compared to the coordination problems it solves. For most teams, that bar is higher than it looks. Are you there yet?</p>
<h2 id="start-with-solid-foundations-before-you-surface-it">Start with solid foundations before you surface it</h2>
<p>Good roads aren’t just tarmac. They have lane markings, speed limits, signs, and traffic lights. All of the things that make roads safe and predictable to navigate. A GPS doesn’t create any of that. It surfaces it. A portal works the same way. It can expose your golden paths, governance, pipelines, and self-service workflows, but it doesn’t define them for you. Get the road right first.</p>
<p>Your platform will consist of tens of tools, each focused on solving problems in its own domain. All of those tools can be configured and integrated in different ways to achieve the outcomes your teams need, and a lot of effort from Platform Engineering teams goes into making them work well together for the organization.</p>
<p>It’s also likely your developers and application teams have worked directly with the platform tools in their careers, too, and while context switching is an issue, it won’t be unfamiliar to them to move between different tools to get their work done. If you can lower the friction they have when working with the underlying platform tools, that’s a huge win, and in my opinion, it’s a signal of a successful platform foundation.</p>
<p>And with the rise of AI, that’s becoming even more relevant. Developers increasingly want to be met where they’re already working. That might be by providing a Command Line Interface, an API call, or an MCP server that the developer—or the agent they are using—can interact with directly. A well-structured platform exposes those interfaces. A portal is one way to surface platform capabilities, but it’s not the only way, and for many developers, it might not even be the preferred one.</p>
<p>You’re probably familiar with core concepts that platform teams focus on: standardization, policies and governance, self-service, and repeatable, consistent delivery across teams and environments. There’s a lot of work that goes into getting those foundations right before they are ready to be surfaced through an interface, and it doesn’t stop once they’re in place. The tools your platform is built on keep evolving. Vendors ship new features, integrations improve, and better primitives emerge. A good platform team takes advantage of that, pushing complexity back onto vendors and partners where possible, and reducing the volume of custom scripts and integrations they need to own and maintain themselves. The point is that building a platform isn’t a 12-month checkbox exercise. It’s a living thing that needs ongoing maintenance, care, and attention. New roads get built, old ones get resurfaced, and the rules of the road change over time. Your platform is no different. The tools keep evolving, your organization’s needs shift, and the work of keeping it solid never really stops.</p>
<p>Adding a portal to a platform will begin to expose the platform’s underlying capabilities as they stand today. If your underlying platform has a strong foundation and delivers measurable benefits to the organization, exposing its capabilities in a portal will amplify the value it already delivers, enabling developers to get faster access to what already works. But the same can be said if your underlying platform is a bit of a mess, too. A portal that surfaces an inconsistent or fragile deployment pipeline doesn’t hide the inconsistency. It just makes it more visible and more frustrating to more people.</p>
<p>A portal amplifies what’s already there. Make sure what’s already there is worth amplifying.</p>
<h2 id="portals-have-a-place">Portals have a place</h2>
<p>When you’re working in an organization with hundreds of developers and many applications, finding the right information is hard. Even just knowing <em>what</em> you have in the organization can be a massive undertaking. Ask a new engineer at most organizations to find the logs for a service they didn’t build, and watch what happens. They’ll ask around, dig through wikis, search Slack, and probably end up messaging whoever seems like they might know. It works, eventually. But it shouldn’t be that hard.</p>
<p>Here at Octopus Deploy, where you could argue we have one core product, there are many Engineering teams, each owning different parts of the application. They all have defined software development processes, source code, internal and external documentation, and several Slack channels. Even though some of that information lives in the same tool across teams - let’s use GitHub as an example for our source code tool - there are still hundreds of repositories internally. There’s also the support and operational lens too, such as where the logs are being sent, what observability is being used by each application, where you file a bug or request a new feature, and who to contact or where to go if you need to ask a question from the team that owns the service.</p>
<p>Sit for a moment and ask these same questions in the context of your company, and consider how long it would take a new engineer on your team to find those answers on their own.</p>
<p>Service discoverability is one use case where a portal can deliver real value without a huge lift for the platform team. The information already exists in your organization; it just needs to be collated and surfaced in one place.</p>
<p>Once your platform foundations are solid, a portal can surface those capabilities in a way that genuinely changes how developers interact with it. Self-service workflows that are repeatable and reliable, golden paths that are well-defined and trusted, pipelines that behave consistently across teams and environments. A portal can bring all of that together in one place and make it accessible without developers needing to know what sits behind each action.</p>
<p>A useful question to ask yourself: if we built a portal today, what would we put in it? If the answer is “not much,” that’s a signal there’s more platform work to do first, or that your company doesn’t even need a portal to make the most of Platform Engineering. If the answer is a long list of capabilities that are already working well and delivering value, you’re probably ready.</p>
<p>This is where the GPS analogy comes full circle. The roads are built, the lane markings are down, and the signs are in place. Now you can hand people a GPS and trust that it will reliably navigate them to where they need to go, on the most efficient route. That’s the version of a portal worth building toward.</p>
<h2 id="roads-first-gps-second-wheres-your-team-at">Roads first, GPS second. Where’s your team at?</h2>
<p>A portal is a powerful navigation tool. But navigation only works when there’s somewhere real to go. Every organization is at a different point on this journey, and this post isn’t a rule book. Some teams genuinely are ready to invest in a portal, and for them, it will pay off. But in my experience, most teams have more platform work to do before the portal delivers on its promise.</p>
<p>The roads come first. The GPS comes when the roads are worth navigating.</p>
<p>So, where’s your team at?</p>
<p>Happy Deployments!</p>]]></content>
    </entry>
    <entry>
      <title>Use Process Templates in Platform Hub to verify and deploy dependencies</title>
      <link href="https://octopus.com/blog/use-process-templates-to-verify-dependencies" />
      <id>https://octopus.com/blog/use-process-templates-to-verify-dependencies</id>
      <published>2026-04-13T00:00:00.000Z</published>
      <updated>2026-04-13T00:00:00.000Z</updated>
      <summary>An example of a Process Template that can be used to verify and deploy dependencies in Octopus Deploy</summary>
      <author>
        <name>Bob Walker, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>In a perfect world, applications are loosely coupled and independently deployable. But the real world is often messy. In service-based architectures, front-end applications need specific versions of back-end services to be deployed to Production first. Often, those back-end services are managed by other teams.</p>
<p>In the past, Octopus Deploy users used the <a href="https://octopus.com/docs/projects/coordinating-multiple-projects/deploy-release-step">Deploy a Release</a> built-in step, the <a href="https://library.octopus.com/step-templates/18392835-d50e-4ce9-9065-8e15a3c30954/actiontemplate-chain-deployment">Chain Deployment</a> step template, or the <a href="https://library.octopus.com/step-templates/0dac2fe6-91d5-4c05-bdfb-1b97adf1e12e/actiontemplate-deploy-child-octopus-deploy-project">Deploy Child Octopus Deploy Project</a> step template to solve this problem. But because they were single steps, the solutions were difficult to configure and brittle.</p>
<p>Platform Hub’s <a href="https://octopus.com/docs/platform-hub/process-templates">Process Templates</a> solve this problem at scale. This post will walk through the new <a href="https://github.com/OctopusSamples/PlatformHubLibrary/tree/main/process-templates/verify-dependencies">Verify Dependencies</a> Process Template.</p>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/verify-dependencies-in-action.png" alt="Verify Dependencies Process Template being used in a deployment"></p></figure>
<h2 id="the-use-cases">The use cases</h2>
<p>I created the <strong>Verify Dependencies</strong> Process Template based on previous experience as an Octopus Deploy user. I automated the deployment of my application, including code, infrastructure, and database components. The coordination and scheduling were still manual.</p>
<h3 id="service-dependencies">Service dependencies</h3>
<p>Before working for Octopus Deploy, I was the lead developer on a loan origination system at a bank. The loan origination system depended on multiple REST API services.</p>
<ul>
<li><strong>Customer Information Service</strong>: Truth center for the customer PII data.</li>
<li><strong>Financial Service</strong>: Truth Center for the customer’s financial information.</li>
<li><strong>Credit Service</strong>: Checked the customer’s credit against the big three credit agencies.</li>
<li><strong>Collateral Service</strong>: Truth center for the customer’s collateral.</li>
<li><strong>Decision Engine Service</strong>: Rules-based engine that would auto-approve the loan or require more verification.</li>
</ul>
<p>Each of those services was managed by a different team, which had different priorities and deployment schedules. The challenge my team ran into is that we’d make a change to our application based on functionality added in version <code>2.1.x</code> of the Financial Service, but Production was running <code>2.0.5</code>.</p>
<p>It required significant coordination and grit to ensure that all the loan origination system’s dependent services were on the correct version in Production before we deployed. There were several close calls and a few extended deployment windows.</p>
<h3 id="release-orchestration">Release orchestration</h3>
<p>Often, a back-end service and the loan origination system were deployed on the same night. For obvious reasons, the deployment of back-end service changes occurred before the loan origination system. The low-friction and fastest solution was to schedule the back-end service’s deployment for 7:00 pm and the loan origination system for 7:30 pm.</p>
<p>That worked until multiple back-end services needed to be deployed on the same night. For example, the collateral service, credit service, and customer information service had changes to deploy on the same night.</p>
<ul>
<li>The collateral service was dependent upon the customer information service.</li>
<li>The credit service was also dependent upon the customer information service.</li>
<li>The collateral and credit services were not dependent upon each other.</li>
</ul>
<p>The result was:</p>
<ul>
<li>Schedule the customer information service deployment for 7:00 pm</li>
<li>Schedule the credit and collateral service deployments for 7:30 pm</li>
<li>Schedule the loan origination system deployment for 8:00 pm</li>
</ul>
<p>All that coordination occurred at a daily Change Approval Board (CAB) meeting at 11:00 am.</p>
<h3 id="an-unsolved-problem">An unsolved problem</h3>
<p>This problem is the silent killer of Continuous Delivery. As a developer, after automating my application’s deployments, I was rarely concerned about production deployments. That is because using Octopus Deploy, my application’s deployment processes were tested multiple times before Production.</p>
<p>What kept me up at night was post-deployment verification. Specifically, if the change depended on other back-end services. Runtime errors that appeared during specific conditions were the most frustrating. It’s easy to debug a missing endpoint or a contract change. It was maddening when all the changes were hidden in the business logic.</p>
<p>Leveraging a message bus wouldn’t have fixed this issue. Yes, that would’ve decoupled hard dependencies between applications. But that doesn’t account for business requirement changes that span multiple applications or services. A new rule may not impact the message contract, but the business logic can change.</p>
<p>The primary reason for creating this Process Template was the lack of a better alternative. I have yet to find a solution that can handle the nuances of dependency management across applications.</p>
<h2 id="a-better-solution-with-process-templates">A better solution with Process Templates</h2>
<p>I’m the author of the <a href="https://library.octopus.com/step-templates/0dac2fe6-91d5-4c05-bdfb-1b97adf1e12e/actiontemplate-deploy-child-octopus-deploy-project">Deploy Child Octopus Deploy Project</a>. I created that step to address limitations in the Deploy a Release and Chain Deployment steps. At the time, working within the constraints of what was possible with a 2021 version of Octopus Deploy, I was happy with the solution.</p>
<p>But the solution had multiple problems.</p>
<p>Creating an approval workflow with that step is incredibly messy for consumers. See the <a href="https://octopus.com/blog/release-management-with-octopus#scenario-approvals-in-parent-project-only">approval in parent project only</a> section in the announcement blog post as an example. A consumer has to:</p>
<ul>
<li>Add the <code>Deploy Child Octopus Deploy Project</code> for each of the dependent projects configured in what-if mode.</li>
<li>Add a manual intervention step and set the instruction message using output variables from the previous step.</li>
<li>Add the <code>Deploy Child Octopus Deploy Project</code> for each of the dependent projects configured for deployment mode. Set the version to deploy to the output variables from the previous step.</li>
</ul>
<p>A project with five dependent projects would require 11 steps just for approval. A release manager attempting to coordinate the release of 50 applications would need a process with 101 steps!</p>
<p>Process Templates enabled a better solution by supporting multiple steps. As the producer of the step, I can abstract away all the complexity from the consumer. Consumers no longer have to add multiple steps for each dependent project. Nor do they have to know the correct Octostache for the output variables. Adding a new dependency is as simple as editing a JSON array.</p>
<p>Now consumers can focus on answering:</p>
<ul>
<li>Which projects are this project dependent upon?</li>
<li>What is the version required for each project?</li>
<li>Do you want to stop the deployment, continue the deployment, or attempt to deploy when a dependency is not in the target environment?</li>
<li>If you choose to deploy, do you want to pause for approval?</li>
<li>If you choose to deploy, in what order should the dependencies be deployed?</li>
</ul>
<h2 id="verify-dependencies-process-template">Verify Dependencies Process Template</h2>
<p>The <strong>Verify Dependencies</strong> Process Template has three steps.</p>
<ol>
<li><strong>Check Dependencies</strong> - Loops through all the dependencies to determine if an appropriate version is in the target environment.</li>
<li><strong>Approve Dependency Result</strong> - When configured, the Process Template pauses the deployment for verification when a dependency doesn’t have an appropriate version in the target environment.</li>
<li><strong>Deploy Dependencies</strong> - When configured, the Process Template will initiate deployments for all the dependencies found in the <strong>Check Dependencies</strong> step.</li>
</ol>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/verify-dependencies-process.png" alt="Verify Dependencies process with three steps"></p></figure>
<p>It has the following parameters.</p>
<ul>
<li><strong>Worker Pool</strong> - The worker pool to execute all the scripts in the Process Template</li>
<li><strong>Octopus Deploy API key</strong> - The API key of a service account that has permissions to view the dependent projects, and if configured, deploy those dependencies.</li>
<li><strong>Target Environment</strong> - The environment to check, and if configured, deploy the dependencies.</li>
<li><strong>Project Dependencies</strong> - A JSON array containing all the dependencies, the version pattern, and deployment order. See the section below for more details.</li>
<li><strong>Default Dependency Action</strong> - Indicates what the Process Template should do if a dependency doesn’t have the appropriate version in the target environment. The possible options are <code>Stop</code>, <code>Continue</code>, <code>Deploy when newer matching found</code>, and <code>Deploy only when no matching found</code>. Default is <code>Stop</code>.</li>
<li><strong>Approval requested to proceed</strong> - Determines if the Process Template should pause and wait for approval when a dependency doesn’t have the appropriate version in the target environment and the dependency action is <code>Continue</code>, <code>Deploy when newer matching found</code>, or <code>Deploy only when no matching found</code>. Default is <code>Yes</code>.</li>
<li><strong>Reuse Change Request Number in Dependencies</strong> - Tells the Process Template to re-use the ITSM change request number when initiating deployments. Only applicable when the dependency action is <code>Deploy when newer matching found</code> or <code>Deploy only when no matching found</code>. Default is <code>No</code>.</li>
<li><strong>Target Tenant</strong> - Only used when verifying a <a href="https://octopus.com/docs/tenants">multi-tenanted dependency</a>. By default, the Process Template ignores this parameter when used in a non-tenanted project. When used in a tenanted project, the Process Template uses the current tenant of the deployment.</li>
</ul>
<h3 id="project-dependencies">Project dependencies</h3>
<p>I opted for a JSON array as a parameter for dependencies. Requiring a consumer to add multiple Process Templates is an awful experience.</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="json"><code><span class="line"><span style="color:#000000">[</span></span>
<span class="line"><span style="color:#000000">  {</span></span>
<span class="line"><span style="color:#0451A5">    "projectName"</span><span style="color:#000000">: </span><span style="color:#A31515">"Spear"</span><span style="color:#000000">,</span></span>
<span class="line"><span style="color:#0451A5">    "versionPattern"</span><span style="color:#000000">: </span><span style="color:#A31515">">1.0.54"</span><span style="color:#000000">,        </span></span>
<span class="line"><span style="color:#0451A5">    "deployGroup"</span><span style="color:#000000">: </span><span style="color:#098658">1</span><span style="color:#000000">,</span></span>
<span class="line"><span style="color:#0451A5">    "promptedVariables"</span><span style="color:#000000">: [</span></span>
<span class="line"><span style="color:#000000">         {</span></span>
<span class="line"><span style="color:#0451A5">             "name"</span><span style="color:#000000">: </span><span style="color:#A31515">"Prompted.Input.Variable"</span><span style="color:#000000">,</span></span>
<span class="line"><span style="color:#0451A5">             "value"</span><span style="color:#000000">: </span><span style="color:#A31515">"Dependency Checker"</span></span>
<span class="line"><span style="color:#000000">         }</span></span>
<span class="line"><span style="color:#000000">     ]</span></span>
<span class="line"><span style="color:#000000">  },</span></span>
<span class="line"><span style="color:#000000">  {</span></span>
<span class="line"><span style="color:#0451A5">    "projectName"</span><span style="color:#000000">: </span><span style="color:#A31515">"TAKA"</span><span style="color:#000000">,</span></span>
<span class="line"><span style="color:#0451A5">    "versionPattern"</span><span style="color:#000000">: </span><span style="color:#A31515">"~2.4.0"</span><span style="color:#000000">,        </span></span>
<span class="line"><span style="color:#0451A5">    "deployGroup"</span><span style="color:#000000">: </span><span style="color:#098658">1</span></span>
<span class="line"><span style="color:#000000">  },</span></span>
<span class="line"><span style="color:#000000">  {</span></span>
<span class="line"><span style="color:#0451A5">    "projectName"</span><span style="color:#000000">: </span><span style="color:#A31515">"TAWA"</span><span style="color:#000000">,</span></span>
<span class="line"><span style="color:#0451A5">    "versionPattern"</span><span style="color:#000000">: </span><span style="color:#A31515">"^4.0.0"</span><span style="color:#000000">,        </span></span>
<span class="line"><span style="color:#0451A5">    "dependencyAction"</span><span style="color:#000000">: </span><span style="color:#A31515">"Continue"</span><span style="color:#000000">,</span></span>
<span class="line"><span style="color:#0451A5">    "deployGroup"</span><span style="color:#000000">: </span><span style="color:#098658">2</span></span>
<span class="line"><span style="color:#000000">  },</span></span>
<span class="line"><span style="color:#000000"> {</span></span>
<span class="line"><span style="color:#0451A5">    "projectName"</span><span style="color:#000000">: </span><span style="color:#A31515">"StoreHub"</span><span style="color:#000000">,</span></span>
<span class="line"><span style="color:#0451A5">    "versionPattern"</span><span style="color:#000000">: </span><span style="color:#A31515">">1.0.3"</span><span style="color:#000000">,</span></span>
<span class="line"><span style="color:#0451A5">    "spaceName"</span><span style="color:#000000">: </span><span style="color:#A31515">"Default"</span><span style="color:#000000">,    </span></span>
<span class="line"><span style="color:#0451A5">    "tenantName"</span><span style="color:#000000">: </span><span style="color:#A31515">"Internal"</span><span style="color:#000000">,    </span></span>
<span class="line"><span style="color:#0451A5">    "deployGroup"</span><span style="color:#000000">: </span><span style="color:#098658">3</span></span>
<span class="line"><span style="color:#000000">  }</span></span>
<span class="line"><span style="color:#000000">]</span></span></code></pre>
<p>The properties for each JSON object are:</p>
<ul>
<li><strong>projectName</strong> (<strong>Required</strong>): the name of the project of the dependency.</li>
<li><strong>versionPattern</strong> (<strong>Required</strong>): the version pattern to match on. See below for more details.</li>
<li><strong>deployGroup</strong> (<strong>Optional</strong>): Projects in the same deploy group are deployed concurrently. Projects in different deploy groups are deployed sequentially. If omitted, all dependencies are deployed sequentially. See below for more details.</li>
<li><strong>spaceName</strong> (<strong>Optional</strong>): overrides the current space.</li>
<li><strong>tenantName</strong> (<strong>Optional</strong>): the name of the tenant to deploy. If not specified, during a multi-tenanted deployment, it will use the current deployment’s tenant.</li>
<li><strong>promptedVariables</strong> (<strong>Optional</strong>): an array that lets you send in prompted variable values to the dependency project. It will only work with string variable types, text, and sensitive values.</li>
<li><strong>dependencyAction</strong> (<strong>Optional</strong>): overrides the default dependency action parameter. For example, if you want to continue for a specific dependency but stop for all other dependencies. The options are:
  - <code>Stop</code>: which will stop the deployment
  - <code>Continue</code>: which will continue the deploy
  - <code>DeployNoMatchingInTarget</code>: which will deploy only when no matching version in the target environment exists.
  - <code>DeployNewerMatching</code>: which will deploy only when the source environment contains a newer matching version.</li>
</ul>
<h3 id="version-pattern">Version pattern</h3>
<p>Versioning is the heart of the <strong>Verify Dependencies</strong> Process Template. Octopus Deploy uses <a href="https://semver.org">SemVer</a> for release versioning. All previous step templates were specific versions (<code>2.2.5</code>), the latest version (<code>2.2.x</code>), or a wildcard (<code>2.2.x</code>).</p>
<p>I can’t predict all the dependencies and their versioning requirements. So, I decided to implement a version pattern similar to Node.js. I modified it slightly because we support .NET and they allow Major.Minor.Patch.Build.</p>
<ul>
<li>Exact version: 1.2.3.4 - only version 1.2.3.4 will be accepted.</li>
<li>Greater than current: >1.2.3.4 - Any version greater than or equal to 1.2.3.4 will be accepted.</li>
<li>Caret range: ^1.2.3.4 - Allows minor, patch, and build updates, locking the major version. Similar to 1.x.</li>
<li>Tilde range: ~1.2.3.4 - Allows patch and build updates only, locking the major and minor versions.</li>
<li>Major wildcard: 2.x - Allows any version or any version within a major range.</li>
</ul>
<p>The <code>.Build</code> part of the version is optional. Version patterns like 1.2.3, >1.2, ~1.2, and 2.x are accepted.</p>
<h3 id="deploy-group">Deploy group</h3>
<p>Earlier in this post, I discussed the deployment order of the back-end services because of these rules:</p>
<ul>
<li>The collateral service was dependent upon the customer information service.</li>
<li>The credit service was also dependent upon the customer information service.</li>
<li>The collateral and credit services were not dependent upon each other.</li>
</ul>
<p>I wanted to make it very easy to deploy projects in a specific order. But, at the same time, allow for concurrent deployments of non-dependent projects. The deploy group property in the JSON object solves for that.</p>
<ul>
<li>All deployments in the group must finish before moving to the next group.</li>
<li>Any projects in the same deploy group can run concurrently.</li>
<li>A failure in a deploy group will prevent subsequent deploy groups from being processed.</li>
</ul>
<p>The deploy group property is optional.</p>
<ul>
<li>If no projects have a deploy group in the JSON array, then deploy the projects in the order they appear in the array.</li>
<li>If a deploy group appears for some objects in the JSON array, deploy those projects based on the specified deploy group. After that, deploy projects without a deploy group in the order they appear in the array.</li>
</ul>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/deploy-groups-in-action.png" alt="Deployments across multiple deploy groups"></p></figure>
<h3 id="respecting-the-task-cap">Respecting the task cap</h3>
<p>The ability to concurrently initiate deployments raised a major concern: the <a href="https://octopus.com/docs/octopus-cloud/task-cap">task cap</a>. It would be very easy to consume all the available tasks quickly.</p>
<p>For example, you have a task cap of 10, and a deploy group with 15 projects. If all 15 projects require deployment, you could end up blocking any other deployments until those 15 are finished. Even worse, what happens if some of those 15 projects trigger a runbook during deployment?  </p>
<p>Before initiating a deployment, the Process Template will ensure that at least three tasks are available. It will check every five seconds until it finds at least three available tasks.</p>
<h3 id="dependency-action-and-approvals">Dependency action and approvals</h3>
<p>Every company, environment, and application is different. When deploying to a testing environment, you might only need a warning if a dependency isn’t on the correct version. But you’d want to fail the deployment if a dependency isn’t on the correct version in pre-production or Production.</p>
<p>The <strong>Verify Dependencies</strong> Process Template will use Octopus Deploy’s API to determine the version of each dependency in the target environment.</p>
<p>In the event a match doesn’t occur, the step can:</p>
<ul>
<li><strong>Stop:</strong> If one or more of the dependency checks fail, it will stop and fail the deployment. This option is the <strong>default.</strong></li>
<li><strong>Continue:</strong> Will proceed with the deployment even if the dependency check fails. Will not attempt to deploy any dependent projects.</li>
<li><strong>Deploy when newer matching found:</strong> Will always deploy the latest matching version from the previous environment.</li>
<li><strong>Deploy only when no matching found:</strong> Will trigger a deployment to the target environment only if the target environment has a matching version.</li>
</ul>
<p>The deploy options control when a deployment will occur. Imagine a dependency with a version pattern set to <code>>4.5.2</code>.</p>
<ul>
<li>Example #1 - The latest version in Production is <code>4.1.2</code>, and in Test it is <code>4.5.7</code>. <strong>Deploy when newer matching found</strong> will deploy <code>4.5.7</code>. <strong>Deploy only when no matching found</strong> will deploy <code>4.5.7.</code></li>
<li>Example #2 - The latest version in Production is <code>4.5.3</code>, and in Test it is <code>4.5.7</code>. <strong>Deploy when newer matching found</strong> will deploy <code>4.5.7</code>. <strong>Deploy only when no matching found</strong> WILL NOT deploy <code>4.5.7</code> because <code>4.5.3</code> in production matches the pattern <code>>4.5.2</code>.</li>
</ul>
<p>It takes time to build trust in a new process. The <strong>Verify Dependencies</strong> Process Template allows you to pause the deployment and wait for a person to approve the changes.</p>
<h3 id="reusing-change-request-numbers">Reusing change request numbers</h3>
<p>The <a href="https://octopus.com/docs/approvals/servicenow">ServiceNow</a> and <a href="https://octopus.com/docs/approvals#efficient-change-management-approvals-with-jira-service-management">Jira Service Management</a> (JSM) ITSM integrations have led to interesting requests and use cases from our customers. One of the most common requests is the ability to create a single change request number and reuse that for all deployments in a deployment window.</p>
<p>The <strong>Verify Dependencies</strong> Process Template, when configured, will send the change request number from the parent project to all dependent projects when a deployment is invoked. It doesn’t matter whether the parent project created the change request or a user supplied the change request number.</p>
<p>There are a few limitations.</p>
<ol>
<li>Projects can use different SNoW or JSM instances. The Process Template’s logic assumes that the parent project and its dependent projects all use the same ITSM instance.</li>
<li>ITSM has to be configured for the dependent projects. The Process Template cannot modify the project settings. The Process Template will never send the change request number to any project without the ITSM settings configured.</li>
</ol>
<h3 id="prompted-variables">Prompted variables</h3>
<p>It is safe to say that at least one project will require a <a href="https://octopus.com/docs/projects/variables/prompted-variables">prompted variables</a> value to be supplied. The challenge is that each dependent project will have different prompted variable names and values. That is why the prompted variables are part of the <strong>Project Dependencies</strong> JSON array.</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="json"><code><span class="line"><span style="color:#000000">[</span></span>
<span class="line"><span style="color:#000000">  {</span></span>
<span class="line"><span style="color:#0451A5">    "projectName"</span><span style="color:#000000">: </span><span style="color:#A31515">"Spear"</span><span style="color:#000000">,</span></span>
<span class="line"><span style="color:#0451A5">    "versionPattern"</span><span style="color:#000000">: </span><span style="color:#A31515">">1.0.54"</span><span style="color:#000000">,        </span></span>
<span class="line"><span style="color:#0451A5">    "deployGroup"</span><span style="color:#000000">: </span><span style="color:#098658">1</span><span style="color:#000000">,</span></span>
<span class="line"><span style="color:#0451A5">    "promptedVariables"</span><span style="color:#000000">: [</span></span>
<span class="line"><span style="color:#000000">         {</span></span>
<span class="line"><span style="color:#0451A5">             "name"</span><span style="color:#000000">: </span><span style="color:#A31515">"Prompted.Input.Variable"</span><span style="color:#000000">,</span></span>
<span class="line"><span style="color:#0451A5">             "value"</span><span style="color:#000000">: </span><span style="color:#A31515">"Dependency Checker"</span></span>
<span class="line"><span style="color:#000000">         }</span></span>
<span class="line"><span style="color:#000000">     ]</span></span>
<span class="line"><span style="color:#000000">  },</span></span>
<span class="line"><span style="color:#000000">  {</span></span>
<span class="line"><span style="color:#0451A5">    "projectName"</span><span style="color:#000000">: </span><span style="color:#A31515">"TAKA"</span><span style="color:#000000">,</span></span>
<span class="line"><span style="color:#0451A5">    "versionPattern"</span><span style="color:#000000">: </span><span style="color:#A31515">"~2.4.0"</span><span style="color:#000000">,        </span></span>
<span class="line"><span style="color:#0451A5">    "deployGroup"</span><span style="color:#000000">: </span><span style="color:#098658">1</span></span>
<span class="line"><span style="color:#000000">  }</span></span>
<span class="line"><span style="color:#000000">]</span></span></code></pre>
<p>The <strong>Verify Dependencies</strong> Process Template will match the provided name in the JSON array to the name or the label of the prompted variable. That is how our CLI handles it, so it makes sense to be consistent.</p>
<h2 id="permissions">Permissions</h2>
<p>The <strong>Verify Dependencies</strong> Process Template requires an API key because it invokes the Octopus Deploy API. I highly recommend using an API key from a new <a href="https://octopus.com/docs/security/users-and-teams/service-accounts">service account</a> with limited permissions.</p>
<p>When configuring the Process Template to <code>Stop</code> or <code>Continue</code>, the recommended roles are:</p>
<ul>
<li>Project Viewer</li>
<li>Environment Viewer</li>
</ul>
<p>When configuring the Process Template to <code>Deploy when newer matching found</code> or <code>Deploy only when no matching found</code>, then the recommended roles are:</p>
<ul>
<li>Project Viewer</li>
<li>Environment Viewer</li>
<li>Deployment Creator</li>
</ul>
<h2 id="scenarios">Scenarios</h2>
<p>The combination of the chosen dependency action, the approval-required setting, and a dependency’s current state can result in 16 possible outcomes for each dependency. Below is a table I created to help keep track of the possible outcomes.</p>
<div class="table-wrap">














































































































































































<table><thead><tr><th>Matching running in Target Env (Prod)</th><th>Matching running in Source Env (Test)</th><th>Source is Newer</th><th>Chosen Action</th><th>Approval Requested</th><th>Failure</th><th>Deployment Needed</th><th>Approval Required</th></tr></thead><tbody><tr><td>No</td><td>No</td><td>N/A</td><td>Stop</td><td>Yes or No</td><td>TRUE</td><td>FALSE</td><td>FALSE</td></tr><tr><td>No</td><td>No</td><td>N/A</td><td>Continue</td><td>Yes</td><td>FALSE</td><td>FALSE</td><td>TRUE</td></tr><tr><td>No</td><td>No</td><td>N/A</td><td>Continue</td><td>No</td><td>FALSE</td><td>FALSE</td><td>FALSE</td></tr><tr><td>No</td><td>No</td><td>N/A</td><td>Deploy*</td><td>Yes or No</td><td>TRUE</td><td>FALSE</td><td>FALSE</td></tr><tr><td>Yes</td><td>N/A (Is first env in lifecycle)</td><td>N/A</td><td>Any</td><td>Yes or No</td><td>FALSE</td><td>FALSE</td><td>FALSE</td></tr><tr><td>No</td><td>Yes</td><td>N/A</td><td>Stop</td><td>Yes or No</td><td>TRUE</td><td>FALSE</td><td>FALSE</td></tr><tr><td>No</td><td>Yes</td><td>N/A</td><td>Continue</td><td>Yes</td><td>FALSE</td><td>FALSE</td><td>TRUE</td></tr><tr><td>No</td><td>Yes</td><td>N/A</td><td>Continue</td><td>No</td><td>FALSE</td><td>FALSE</td><td>FALSE</td></tr><tr><td>No</td><td>Yes</td><td>N/A</td><td>Deploy*</td><td>Yes</td><td>FALSE</td><td>TRUE</td><td>TRUE</td></tr><tr><td>No</td><td>Yes</td><td>N/A</td><td>Deploy*</td><td>No</td><td>FALSE</td><td>TRUE</td><td>FALSE</td></tr><tr><td>Yes</td><td>Yes</td><td>No</td><td>Any</td><td>Yes or No</td><td>FALSE</td><td>FALSE</td><td>FALSE</td></tr><tr><td>Yes</td><td>Yes</td><td>Yes</td><td>Stop</td><td>Yes or No</td><td>FALSE</td><td>FALSE</td><td>FALSE</td></tr><tr><td>Yes</td><td>Yes</td><td>Yes</td><td>Continue</td><td>Yes or No</td><td>FALSE</td><td>FALSE</td><td>FALSE</td></tr><tr><td>Yes</td><td>Yes</td><td>Yes</td><td>DeployNoMatchingInTarget</td><td>Yes or No</td><td>FALSE</td><td>FALSE</td><td>FALSE</td></tr><tr><td>Yes</td><td>Yes</td><td>Yes</td><td>DeployNewerMatching</td><td>Yes</td><td>FALSE</td><td>TRUE</td><td>TRUE</td></tr><tr><td>Yes</td><td>Yes</td><td>Yes</td><td>DeployNewerMatching</td><td>No</td><td>FALSE</td><td>TRUE</td><td>FALSE</td></tr></tbody></table></div>
<p>It can be hard to visualize results from a table. I set up my instance with a project that depends on these projects.</p>
<ul>
<li>Spear</li>
<li>TAKA</li>
<li>TAWA</li>
<li>StoreHub - multi-tenanted</li>
</ul>
<p>The dependency configuration is:</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="json"><code><span class="line"><span style="color:#000000">[</span></span>
<span class="line"><span style="color:#000000">  {</span></span>
<span class="line"><span style="color:#0451A5">    "projectName"</span><span style="color:#000000">: </span><span style="color:#A31515">"Spear"</span><span style="color:#000000">,    </span></span>
<span class="line"><span style="color:#0451A5">    "versionPattern"</span><span style="color:#000000">: </span><span style="color:#A31515">">1.0.56"</span><span style="color:#000000">,        </span></span>
<span class="line"><span style="color:#0451A5">    "deployGroup"</span><span style="color:#000000">: </span><span style="color:#098658">1</span><span style="color:#000000">    </span></span>
<span class="line"><span style="color:#000000">  },</span></span>
<span class="line"><span style="color:#000000">  {</span></span>
<span class="line"><span style="color:#0451A5">    "projectName"</span><span style="color:#000000">: </span><span style="color:#A31515">"TAKA"</span><span style="color:#000000">,    </span></span>
<span class="line"><span style="color:#0451A5">    "versionPattern"</span><span style="color:#000000">: </span><span style="color:#A31515">"~2.4.0"</span><span style="color:#000000">,    </span></span>
<span class="line"><span style="color:#0451A5">    "deployGroup"</span><span style="color:#000000">: </span><span style="color:#098658">1</span></span>
<span class="line"><span style="color:#000000">  },</span></span>
<span class="line"><span style="color:#000000">  {</span></span>
<span class="line"><span style="color:#0451A5">    "projectName"</span><span style="color:#000000">: </span><span style="color:#A31515">"TAWA"</span><span style="color:#000000">,</span></span>
<span class="line"><span style="color:#0451A5">    "versionPattern"</span><span style="color:#000000">: </span><span style="color:#A31515">"^4.0.0"</span><span style="color:#000000">,        </span></span>
<span class="line"><span style="color:#0451A5">    "deployGroup"</span><span style="color:#000000">: </span><span style="color:#098658">2</span></span>
<span class="line"><span style="color:#000000">  },</span></span>
<span class="line"><span style="color:#000000"> {</span></span>
<span class="line"><span style="color:#0451A5">    "projectName"</span><span style="color:#000000">: </span><span style="color:#A31515">"StoreHub"</span><span style="color:#000000">,    </span></span>
<span class="line"><span style="color:#0451A5">    "versionPattern"</span><span style="color:#000000">: </span><span style="color:#A31515">">1.0.5"</span><span style="color:#000000">,        </span></span>
<span class="line"><span style="color:#0451A5">    "tenantName"</span><span style="color:#000000">: </span><span style="color:#A31515">"Internal"</span><span style="color:#000000">,</span></span>
<span class="line"><span style="color:#0451A5">    "deployGroup"</span><span style="color:#000000">: </span><span style="color:#098658">4</span></span>
<span class="line"><span style="color:#000000">  }</span></span>
<span class="line"><span style="color:#000000">]</span></span></code></pre>
<p>Right now, only Spear doesn’t have a version in Test or Production that matches the version pattern.</p>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/current-state-of-applications.png" alt="The current state of the dependent projects"></p></figure>
<h3 id="warning-with-approval">Warning with approval</h3>
<p>In this scenario, I’ve configured the <strong>Verify Dependencies</strong> Process Template parameters to be:</p>
<ul>
<li><strong>Default Dependency Action</strong>: Continue</li>
<li><strong>Approval requested to proceed</strong>: Yes</li>
</ul>
<p>The result of a deployment to the test environment was:</p>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/warning-with-approval.png" alt="Dependency warning with approval"></p></figure>
<p>Turning off approval would yield the same result, but without the manual intervention step.</p>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/warning-no-approval.png" alt="Dependency warning without an approval"></p></figure>
<h3 id="stopping-deployments">Stopping deployments</h3>
<p>In this scenario, I’ve configured the <strong>Verify Dependencies</strong> Process Template parameters to be:</p>
<ul>
<li><strong>Default Dependency Action</strong>: Stop</li>
<li><strong>Approval requested to proceed</strong>: Yes</li>
</ul>
<p>The result of a deployment to the test environment was:</p>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/failed-deployment.png" alt="Dependency causing a failed deployment"></p></figure>
<h3 id="deploying-dependencies">Deploying dependencies</h3>
<p>In this scenario, version <code>1.0.56</code> for the Spear application exists in the testing environment.</p>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/dependency-with-matching-version-in-test-but-not-prod.png" alt="Dependency with a matching version in test but not production"></p></figure>
<p>I want to deploy that to the production environment. The parameters are updated to be:</p>
<ul>
<li><strong>Default Dependency Action</strong>: Deploy when newer matching found</li>
<li><strong>Approval requested to proceed</strong>: Yes</li>
<li><strong>Reuse Change Request Number in Dependencies</strong>: Yes</li>
</ul>
<p>The deployment to the test environment first checks for problems.</p>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/all-dependencies-are-in-test.png" alt="All necessary dependencies are in test"></p></figure>
<p>When <code>Verify Dependencies</code> runs in production, it will see that <code>1.0.56</code> needs to be deployed and pause the deployment waiting for approval.</p>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/dependency-deployment-approval-required.png" alt="Dependency deployment requiring approvals before proceeding"></p></figure>
<p>After clicking the <code>Proceed</code> button, the deployment will begin. Note that this project also required ITSM approval before the deployment could start.</p>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/deployment-initiated-with-itsm-approval.png" alt="Deployment initiated after approval"></p></figure>
<p>Spear also requires ITSM approval. Because of the parent project configuration, the Process Template sends that change request number to Spear’s production deployment.</p>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/itsm-change-request-sent-to-dependency.png" alt="Deployment initiated with ITSM change request number"></p></figure>
<h3 id="skipping-unnecessary-deployments">Skipping unnecessary deployments</h3>
<p>In this scenario, version <code>1.0.57</code> for the Spear application exists in the Test environment.</p>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/newer-version-than-pattern.png" alt="Dependency has a newer version than the pattern"></p></figure>
<p>The version pattern is still <code>>1.0.56</code>, but the parameters have been changed to:</p>
<ul>
<li><strong>Default Dependency Action</strong>: Deploy only when no matching found</li>
<li><strong>Approval requested to proceed</strong>: Yes</li>
<li><strong>Reuse Change Request Number in Dependencies</strong>: Yes</li>
</ul>
<p>The parent project will not deploy Spear <code>1.0.57</code> to Production because it already has a version in Production that matches the pattern <code>>1.0.56</code>.</p>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/skipping-deployment-even-when-something-is-newer.png" alt="Skipping the deployment even when something is newer"></p></figure>
<h3 id="storing-dependencies-json-array-outside-of-octopus-deploy">Storing dependencies JSON array outside of Octopus Deploy</h3>
<p>One advantage of using a JSON array for the project dependencies is the ability to use a prompted variable for the <strong>Project Dependencies</strong> parameter.</p>
<figure><p><img src="/blog/img/use-process-templates-to-verify-dependencies/prompted-variables-dependency-checker.png" alt="Project dependencies stored as a prompted variable"></p></figure>
<p>You’d do that so you could initiate a deployment using a script + Octopus Deploy’s CLI. Where that JSON Array is created or stored is up to you.</p>
<pre class="astro-code light-plus" style="background-color:#FFFFFF;color:#000000; overflow-x: auto;" tabindex="0" data-language="powershell"><code><span class="line"><span style="color:#001080">$projectDependencies</span><span style="color:#000000"> = </span><span style="color:#A31515">@'</span></span>
<span class="line"><span style="color:#A31515">[</span></span>
<span class="line"><span style="color:#A31515">  {</span></span>
<span class="line"><span style="color:#A31515">    "projectName": "Spear",</span></span>
<span class="line"><span style="color:#A31515">    "versionPattern": ">1.0.56",        </span></span>
<span class="line"><span style="color:#A31515">    "deployGroup": 1,</span></span>
<span class="line"><span style="color:#A31515">  },</span></span>
<span class="line"><span style="color:#A31515">  {</span></span>
<span class="line"><span style="color:#A31515">    "projectName": "TAKA",</span></span>
<span class="line"><span style="color:#A31515">    "versionPattern": "~2.4.0",        </span></span>
<span class="line"><span style="color:#A31515">    "deployGroup": 1</span></span>
<span class="line"><span style="color:#A31515">  },</span></span>
<span class="line"><span style="color:#A31515">  {</span></span>
<span class="line"><span style="color:#A31515">    "projectName": "TAWA",</span></span>
<span class="line"><span style="color:#A31515">    "versionPattern": "^4.0.0",        </span></span>
<span class="line"><span style="color:#A31515">    "deployGroup": 2</span></span>
<span class="line"><span style="color:#A31515">  },</span></span>
<span class="line"><span style="color:#A31515"> {</span></span>
<span class="line"><span style="color:#A31515">    "projectName": "StoreHub",</span></span>
<span class="line"><span style="color:#A31515">    "versionPattern": ">1.0.3",        </span></span>
<span class="line"><span style="color:#A31515">    "tenantName": "Internal",</span></span>
<span class="line"><span style="color:#A31515">    "deployGroup": 3</span></span>
<span class="line"><span style="color:#A31515">  }</span></span>
<span class="line"><span style="color:#A31515">]</span></span>
<span class="line"><span style="color:#A31515">'@</span></span>
<span class="line"><span style="color:#001080">$projectVersionToDeploy</span><span style="color:#000000"> = </span><span style="color:#A31515">"0.0.74"</span></span>
<span class="line"><span style="color:#001080">$projectName</span><span style="color:#000000"> = </span><span style="color:#A31515">"Dependency Checker"</span></span>
<span class="line"><span style="color:#001080">$spaceName</span><span style="color:#000000"> = </span><span style="color:#A31515">"Default"</span></span>
<span class="line"><span style="color:#001080">$environmentName</span><span style="color:#000000"> = </span><span style="color:#A31515">"Test"</span></span>
<span class="line"><span style="color:#001080">$apiKey</span><span style="color:#000000"> = </span><span style="color:#A31515">"Your API Key"</span></span>
<span class="line"><span style="color:#001080">$octopusServer</span><span style="color:#000000"> = </span><span style="color:#A31515">"https://Octopus.YourDomain.com"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#000000">&#x26; octopus login --api-key </span><span style="color:#001080">$apiKey</span><span style="color:#000000"> --server </span><span style="color:#001080">$octopusServer</span></span>
<span class="line"><span style="color:#000000">&#x26; octopus release deploy --project </span><span style="color:#001080">$projectName</span><span style="color:#000000"> --version </span><span style="color:#001080">$projectVersionToDeploy</span><span style="color:#000000"> --environment </span><span style="color:#001080">$environmentName</span><span style="color:#000000"> --variable </span><span style="color:#A31515">"ProjectDependencies:</span><span style="color:#001080">$projectDependencies</span><span style="color:#A31515">"</span><span style="color:#000000"> --space </span><span style="color:#001080">$spaceName</span><span style="color:#000000"> --no-prompt</span></span></code></pre>
<h2 id="installing-the-verify-dependencies-process-template">Installing the Verify Dependencies Process Template</h2>
<p>To install the <strong>Verify Dependencies</strong> Process Template, you’ll need to:</p>
<ul>
<li>Configure <a href="https://octopus.com/docs/platform-hub">Platform Hub</a> on your Octopus Deploy instance.
  - <strong>Important</strong> Platform Hub is available to customers with <code>Enterprise</code> licenses running <code>2025.4</code> or newer.
  - If you are not on an <code>Enterprise</code> license and wish to try out Platform Hub, reach out to <a href="mailto:sales@octopus.com">sales@octopus.com</a>.
  - If you are not running <code>2025.4</code> or newer, then you can download the latest version of Octopus Deploy on our <a href="https://octopus.com/downloads">downloads page</a></li>
<li>Copy the <code>deploy-process-verify-dependencies.ocl</code> from our <a href="https://github.com/OctopusSamples/PlatformHubLibrary/tree/main/process-templates/verify-dependencies">Samples Platform Hub Library GitHub Repository</a> to the <code>.octopus/process-templates</code> in your Platform Hub git repository.</li>
<li>Go to Platform Hub in the Octopus Deploy UI and <a href="https://octopus.com/docs/platform-hub/templates/publishing-and-sharing">publish the new Process Template</a>.</li>
</ul>
<h2 id="release-orchestration-for-a-project-per-component">Release orchestration for a project per component</h2>
<p>If you’ve been a long-time reader of Octopus Deploy’s blog, you likely read the blog post <a href="https://octopus.com/blog/release-management-with-octopus">Better Release Management with Octopus Deploy</a>. In that blog post, I said that a project per application component was the best solution.</p>
<p>For example, an application has four components, so it’d have five projects.</p>
<ul>
<li>Database</li>
<li>Scheduling Service</li>
<li>Web API</li>
<li>Web UI</li>
<li>Orchestration</li>
</ul>
<p>That blog post reflected Octopus Deploy as it existed in 2021. Upon reflection, it was a bad recommendation. It significantly increases complexity for scenarios that rarely occur in the real world. In addition, we’ve added numerous features and functionality since 2021. For example, Process Templates make it easier than ever to scale complex deployment processes to 1000s of applications. With Process Template’s <a href="https://octopus.com/docs/platform-hub/templates/publishing-and-sharing">versioning capabilities</a>, it is easy to push out a change that skips the deployment step if nothing has changed.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The <strong>Verify Dependencies</strong> Process Template demonstrates how to create robust deployment processes at scale using Octopus Deploy’s Platform Hub functionality. Unlike my Deploy Child Octopus Deploy Project, it enabled me to abstract away many decisions and complexity from consumers like you. With step templates, I’d be required to provide extensive documentation, examples, or even videos to show how to create a complex approval process. Now, consumers only need to add the template to a project and populate a few sensible parameters.</p>
<p>Happy deployments!</p>]]></content>
    </entry>
    <entry>
      <title>Continuous Delivery Office Hours Ep.3: Branching strategies</title>
      <link href="https://octopus.com/blog/continuous-delivery-office-hours-e3" />
      <id>https://octopus.com/blog/continuous-delivery-office-hours-e3</id>
      <published>2026-04-07T00:00:00.000Z</published>
      <updated>2026-04-07T00:00:00.000Z</updated>
      <summary>Your branching strategy can support Continuous Delivery, or make it an impossible goal. We discuss the pros and cons of different approaches.</summary>
      <author>
        <name>Steve Fenton, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>Your branching strategy can support Continuous Delivery, or make it an impossible goal. You should assess the impact of how you branch on your ability to deliver software at all times, and you’ll find some branching techniques that work, while others that make software delivery more like walking in the dark through a field of rakes.</p>
<p>Continuous Integration is the practice of integrating code changes frequently, typically multiple times a day. This means you should check your code multiple times a day and keep your main branch deployable. Continuous Integration is a prerequisite for Continuous Delivery.</p>
<p>The name “Continuous Integration” provides solid hints about the crucial parts of the practice. Everyone should <em>integrate</em> their code into a shared branch (feature branches don’t count) and this should be done <em>continuously</em>, which means you’re doing it all the time; at least once a day, but ideally more often.</p>
<p>You’ll often speak to developers who want to stretch the definition of Continuous Integration, but you can’t escape those foundations. Merging the main branch into a long-lived branch feels like Continuous Integration, but you’ll notice no changes come back for ages until someone finally merges to main. Then you have to perform a large, complex merge, which you should avoid.</p>
<p>The <a href="https://dora.dev/capabilities/continuous-delivery/">DORA research on Continuous Delivery</a> includes the capabilities of Continuous Integration and trunk-based development. The statistics suggest you can get similar benefits as long as you limit yourself to 3 (or fewer) short-lived branches (less than a day old).</p>
<h2 id="watch-the-episode">Watch the episode</h2>
<p>You can watch the episode below, or read on to find some of the key discussion points.</p>
<p><a href="https://www.youtube.com/watch?v=WDHZAHGxpR8">Watch Continuous Delivery Office Hours Ep.3</a></p>
<h2 id="the-worst-branching-strategy">The worst branching strategy</h2>
<p>Since the ability to branch code was invented, developers have applied a great deal of creativity to how to use branches. The most common approach is feature branching, where each feature gets a branch of main that evolves separately until the feature is complete, and it’s merged back into main. Release branching allows development to continue on main by taking a cut of the main branch that will be released. This allows hotfixes to be applied to the release branch without further destabilizing it.</p>
<p>The utility of branching strategies is often eroded by the coordination overhead of maintaining the separate branches and merging different changes back together. The more complex the branching strategy, the more likely it is that you’ll have merge conflicts and lost bug fixes. For example, you might fix a release branch and forget to merge the fix back to main, so the next release reintroduces the bug.</p>
<p>This is why the worst branching strategy is Gitflow. This is a complicated branching strategy that creates dedicated branches for features, releases, and hotfixes alongside permanent main and develop branches. The overhead of Gitflow vastly outweighs its benefits.</p>
<p>This is where trunk-based development shines, as it removes unnecessary complexity.</p>
<h2 id="the-best-strategy-is-trunk-based-development">The best strategy is trunk-based development</h2>
<p>Trunk-based development is the process of making all commits directly to the main branch. This is complemented by out-of-band reviews that don’t block merging and by feature toggles that decouple deployments from releases. It should be possible to deploy your software from the main branch at all times, even if features aren’t complete.</p>
<p>The DORA research allows up to 3 short-lived branches, which can be useful for teams working remotely (like open-source project teams), who can use branches and pull requests to coordinate their work. Even so, the goal is to keep branches short-lived and to merge frequently.</p>
<p>Trunk-based development is complemented by automated builds and checks that run when code is committed to the main branch. If a problem is found during these checks, the team should prioritize the fix over other development work.</p>
<h2 id="elite-performance-comes-from-small-batches">Elite performance comes from small batches</h2>
<p>Teams with the best software delivery performance work in small steps. Trunk-based development and Continuous Integration are crucial practices for controlling batch size. Problems are discovered sooner and are easier to fix when you only have a small amount of change to reason about.</p>
<p>Making frequent commits makes it easy to back out a bad change. If a test fails, you can discard changes since the last commit and try again, instead of trying to debug the problem. This is especially true when using AI coding assistants or other code generation techniques.</p>
<p>Ultimately, for trunk-based development to succeed without friction, the entire team must be aligned on the process. It doesn’t work if only some of the team are on board.</p>
<p>Happy deployments!</p>
<div class="hint"><p>Continuous Delivery Office Hours is a series of conversations about software delivery, with Tony Kelly, Bob Walker, and Steve Fenton.</p><p>You can find more episodes on <a href="https://www.youtube.com/playlist?list=PLAGskdGvlaw3CrxkUOAMmiy928lr5D4oh">YouTube</a>, <a href="https://podcasts.apple.com/us/podcast/continuous-delivery-office-hours/id1872101651">Apple Podcasts</a>, and <a href="https://pca.st/hwjaox59">Pocket Casts</a>.</p></div>]]></content>
    </entry>
    <entry>
      <title>Proactive Dependency Security Best Practices</title>
      <link href="https://octopus.com/blog/dependency-security" />
      <id>https://octopus.com/blog/dependency-security</id>
      <published>2026-04-06T00:00:00.000Z</published>
      <updated>2026-04-06T00:00:00.000Z</updated>
      <summary>Learn how to proactively manage and secure your software from vulnerabilities and supply chain attacks.</summary>
      <author>
        <name>Matthew Casperson, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>With the <a href="https://www.elastic.co/security-labs/axios-one-rat-to-rule-them-all">news of the supply chain attack on the axios npm package</a>, more than a few DevOps teams will be scrambling to understand their exposure and identify potentially affected applications. These kinds of attacks are just a fact of life, though, and while serious, simple changes to your deployment pipelines allow teams deploying applications to respond to these events in a more proactive and predictable manner.</p>
<p>In this post, I’ll show you how to proactively manage the risk associated with dependencies and supply chain attacks by associating Software Bill of Materials (SBOM) with deployed applications and automatically scanning those SBOMs for vulnerabilities.</p>
<p>You can complete the steps in this post in around 30 minutes.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Sign up for a free trial of Octopus Cloud at <a href="https://octopus.com/start">https://octopus.com/start</a>. The cloud-hosted version of Octopus is the easiest way to get started, as it doesn’t require any additional configuration to work with the Octopus AI Assistant.</p>
<p>Then install the Octopus AI Assistant Chrome extension from the <a href="https://chromewebstore.google.com/detail/octopus-ai-assistant/agfpjjibnieiihjoehophlbamcifdfha">Chrome Web Store</a>.</p>
<h2 id="creating-the-sample-application">Creating the sample application</h2>
<p>To demonstrate the process of proactively managing and securing your application, we’ll create one of the sample applications provided by the AI Assistant.</p>
<p>Open the AI Assistant and click the <code>Community Dashboards...</code> link:</p>
<p><img src="/blog/img/dependency-security/community-dashboards.png" alt="AI Assistant menu"></p>
<p>Click the <code>Octopus Easy Mode</code> link:</p>
<p><img src="/blog/img/dependency-security/easy-mode.png" alt="Easy Mode option"></p>
<p>Octopus Easy Mode provides the ability to create sample projects based on best practices. We’ll create the sample Kubernetes project. Select the <code>Kubernetes</code> item and click the <code>Execute</code> button:</p>
<p><img src="/blog/img/dependency-security/easy-mode-dashboard.png" alt="Easy Mode option"></p>
<p>Once you review and approve the changes, this results in a project called <code>My K8s WebApp</code> being created in your space, along with all supporting resources like feeds, targets, environments, lifecycles, and accounts.</p>
<p>This sample project demonstrates proactive dependency management, and it starts with lifecycles and environments.</p>
<h2 id="security-environment-and-lifecycles">Security environment and lifecycles</h2>
<p>The AI Assistant created four environments: <code>Development</code>, <code>Test</code>, <code>Production</code>, and <code>Security</code>.</p>
<p>The first three environments represent the typical infrastructure used to host application deployments. The <code>Security</code> environment is a specialized environment that will host our dependency scanning and vulnerability management steps:</p>
<p><img src="/blog/img/dependency-security/environments.png" alt="Octopus environments"></p>
<p>We then have a <code>DevSecOps</code> lifecycle that has four phases, one for each environment. Deployments are executed automatically to the <code>Development</code> and <code>Security</code> environments. This means when a deployment to the <code>Production</code> environment succeeds, it automatically starts a deployment in the <code>Security</code> environment. This will be important later on:</p>
<p><img src="/blog/img/dependency-security/lifecycles.png" alt="Octopus lifecycles"></p>
<h2 id="the-deployment-process">The deployment process</h2>
<p>The deployment process for the sample application involves scanning the SBOM for a given application version. The final step, called <code>Scan for Vulnerabilities</code>, accepts a package containing an SBOM file and scans it with an open-source dependency-scanning tool.</p>
<p>Notably, the steps that deploy the application (<code>Deploy a Kubernetes Web App via YAML</code> in this example) skip the <code>Security</code> environment. This means deployments to all environments perform the security scan, but deployments to the <code>Security</code> environment <em>only</em> scan the SBOM file:</p>
<p><img src="/blog/img/dependency-security/deployment-process.png" alt="Octopus deployment process"></p>
<p>The end result of this deployment process is that every deployment to every environment performs an SBOM security scan, and once a deployment to the <code>Production</code> environment succeeds, a deployment is immediately triggered in the <code>Security</code> environment.</p>
<p>This initial sequence of a deployment to <code>Production</code> followed by a deployment to <code>Security</code> is not particularly useful, as it is unlikely that a new vulnerability was detected in the seconds between the deployments.</p>
<p>However, the deployment to the <code>Security</code> environment can then be rerun as part of a trigger.</p>
<h2 id="rerunning-the-security-scan-as-a-trigger">Rerunning the security scan as a trigger</h2>
<p>The sample project also includes a <code>Daily Security Scan</code> trigger. This trigger reruns the deployment in the <code>Security</code> environment once per day:</p>
<p><img src="/blog/img/dependency-security/triggers.png" alt="Octopus triggers"></p>
<p>This results in a daily scan of the dependencies that contributed to the version of your application in the <code>Production</code> environment.</p>
<p>This demonstrates how a Continuous Deployment (CD) tool like Octopus complements Continuous Integration (CI) tools to implement DevSecOps. Because Octopus knows exactly which versions of your applications are currently in production (as opposed to the state of a dependency lock file in Git, which may not reflect code deployed to production), it can scan application dependencies to catch vulnerabilities as soon as they are discovered.</p>
<p>You can then automatically respond to any vulnerability reports with custom steps like email alerts or messages sent to a chat platform, and proactively address any issues in a predictable and controlled manner.</p>
<h2 id="next-steps">Next steps</h2>
<p>Scanning an SBOM for vulnerabilities is just one example of risk management that is possible once you associate SBOMs with deployed applications and create an environment where security tasks can be run.</p>
<p>For example, you could scan the SBOM for known dependencies, even when there is no CVE associated with them, or generate reports on outdated dependencies.</p>
<h2 id="what-just-happened">What just happened?</h2>
<p>By following along with this post, you created a sample Kubernetes application with:</p>
<ul>
<li>Deployments that captured both application code and its associated SBOM</li>
<li>SBOM security scanning steps</li>
<li>Environments and lifecycles that support proactive security management</li>
<li>A trigger that performs a daily security scan of the dependencies in your production application</li>
</ul>
<p>This pattern allows DevOps teams to determine what dependencies were included in a deployment and use that information to respond to known dependency vulnerabilities. This workflow complements other security practices such as SAST/DAST scanning at CI-time, dependency management, and runtime scanning to decrease the risks associated with dependency vulnerabilities and supply chain attacks.</p>]]></content>
    </entry>
    <entry>
      <title>Practical Platform Engineering in 5 Lunches: 5. Policies</title>
      <link href="https://octopus.com/blog/platform-engineering-lunch-5" />
      <id>https://octopus.com/blog/platform-engineering-lunch-5</id>
      <published>2026-04-03T00:00:00.000Z</published>
      <updated>2026-04-03T00:00:00.000Z</updated>
      <summary>Learn how to use policies to provide guardrails for projects to ensure they are compliant with organizational standards.</summary>
      <author>
        <name>Matthew Casperson, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>Practical Platform Engineering in 5 Lunches is a series of blog posts that takes you through the process of building a hands-on Internal Developer Platform (IDP) using Octopus Deploy.</p>
<p>This is part 5 of the series. In the <a href="/blog/platform-engineering-lunch-4">previous post</a>, you created a project structure that supports a shared responsibility model, with multiple teams owning different stages of the deployment process.</p>
<p>In this post, you’ll learn about policies, which provide guardrails for projects to ensure they are compliant with organizational standards.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Sign up for a free trial of Octopus Cloud at <a href="https://octopus.com/start">https://octopus.com/start</a>. The cloud-hosted version of Octopus is the easiest way to get started, as it doesn’t require any additional configuration to work with the Octopus AI Assistant.</p>
<p>Then install the Octopus AI Assistant Chrome extension from the <a href="https://chromewebstore.google.com/detail/octopus-ai-assistant/agfpjjibnieiihjoehophlbamcifdfha">Chrome Web Store</a>.</p>
<h2 id="exploring-the-sample-policy">Exploring the sample policy</h2>
<p>The mock Git repo shares sample policies that you can apply to your projects. Open Platform Hub, click <code>Policies</code>, and click the <code>Manual Intervention Required</code> policy:</p>
<p><img src="/blog/img/platform_engineering_lunch_5/policies.png" alt="Sample manual intervention policy"></p>
<p>This sample policy requires that any deployment include a manual intervention step. To activate the policy, it must be published and set to active:</p>
<p><img src="/blog/img/platform_engineering_lunch_5/publish.png" alt="Publishing a policy"></p>
<p>To trigger a policy violation, you must delete the manual intervention step from the deployment process. Open the process for the <code>K8s Web App</code> project, delete the <code>Manual Intervention</code> step, and save the project:</p>
<p><img src="/blog/img/platform_engineering_lunch_5/delete-step.png" alt="Manual intervention step removed"></p>
<p>Save the changes and deploy a release. The deployment will fail because the process doesn’t include a manual intervention step, which violates the policy you just published:</p>
<p><img src="/blog/img/platform_engineering_lunch_5/error.png" alt="Deployment failed due to policy violation"></p>
<p>This demonstrates how platform teams can enforce organizational standards and provide guardrails for projects using policies. Policies are another example of architectural decisions shared and maintained at scale through Platform Hub to support DevOps teams.</p>
<p>The <code>Suggest a fix</code> button can be used by teams that may have encountered an error due to a policy violation. This feature scans the deployment logs, passes the content to an LLM, and provides guidance on resolving the issue:</p>
<p><img src="/blog/img/platform_engineering_lunch_5/suggest-a-fix.png" alt="Suggest a fix"></p>
<h2 id="what-just-happened">What just happened?</h2>
<p>At the end of this post, you have:</p>
<ul>
<li>Published a policy that requires all deployment processes to include a manual intervention step.</li>
<li>Modified the deployment process for the <code>K8s Web App</code> project to intentionally violate the policy.</li>
<li>Attempted to deploy a release, which failed because the deployment process violated the policy.</li>
<li>Used the <code>Suggest a fix</code> button to resolve the policy violation.</li>
</ul>
<h2 id="whats-next">What’s next?</h2>
<p>Congratulations! You have completed the blog series.</p>
<p>By following along with the series, you have built a hands-on Internal Developer Platform (IDP) using Octopus Deploy. You have also experienced how the strong opinions baked into Platform Hub complement the traditional role of CI servers, enabling platform teams to share and maintain architectural decisions at scale.</p>]]></content>
    </entry>
    <entry>
      <title>Practical Platform Engineering in 5 Lunches: 4. Shared Responsibility of Projects</title>
      <link href="https://octopus.com/blog/platform-engineering-lunch-4" />
      <id>https://octopus.com/blog/platform-engineering-lunch-4</id>
      <published>2026-04-02T00:00:00.000Z</published>
      <updated>2026-04-02T00:00:00.000Z</updated>
      <summary>Learn how to structure projects to support multiple teams owning different parts of the deployment process.</summary>
      <author>
        <name>Matthew Casperson, Octopus Deploy</name>
      </author>
      <content type="html"><![CDATA[<p>Practical Platform Engineering in 5 Lunches is a series of blog posts that takes you through the process of building a hands-on Internal Developer Platform (IDP) using Octopus Deploy.</p>
<p>This is part 4 of the series. In the <a href="/blog/platform-engineering-lunch-3">previous post</a>, you pushed changes to a shared process template in Platform Hub and saw how those changes were automatically consumed by the project that used the template.</p>
<p>In this post, you’ll build a project that supports a shared responsibility model, with multiple teams owning different stages of a deployment.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Sign up for a free trial of Octopus Cloud at <a href="https://octopus.com/start">https://octopus.com/start</a>. The cloud-hosted version of Octopus is the easiest way to get started, as it doesn’t require any additional configuration to work with the Octopus AI Assistant.</p>
<p>Then install the Octopus AI Assistant Chrome extension from the <a href="https://chromewebstore.google.com/detail/octopus-ai-assistant/agfpjjibnieiihjoehophlbamcifdfha">Chrome Web Store</a>.</p>
<h2 id="updating-the-project-structure">Updating the project structure</h2>
<p>Open the process for the <code>K8s Web App</code> project. You already have the <code>Platform PreDeploy Hook</code> step template as the first step. This effectively means the platform team owns the start of the deployment process.</p>
<p>:::div{.hint} You must publish process templates before they can be used in a project. See the <a href="/blog/platform-engineering-lunch-2">second post</a> for an example of publishing process templates. :::</p>
<p>Add the <code>Security PreDeploy Hook</code> process template next. This gives the security team the opportunity to implement any security checks or processes that must be run before the deployment can proceed.</p>
<p>Add the <code>Security PostDeploy Hook</code> and <code>Platform PostDeploy Hook</code> process templates as the final two steps. This allows the security and platform teams to implement any checks or processes that must run after the deployment completes.</p>
<p>All process template steps default to the name <code>Run a Process Template</code>, but must have a unique name to save the process. Rename each of the process template steps under the <code>Settings</code> tab and define a new name in the <code>Name</code> field:</p>
<p><img src="/blog/img/platform_engineering_lunch_4/process-template-name.png" alt="Process template step name"></p>
<p>You must also configure the worker pool parameter for each of the process templates.</p>
<p>With these steps in place, you have modelled your project to support the cross-cutting concerns of both the platform and security teams. This embraces Conway’s law, which posits that organizations design systems that mirror their own communication structure. Conway’s law is sometimes seen as an unintended consequence arising in organizations, but regardless, it is a common pattern that must be supported by platform teams.</p>
<p>By wrapping deployment processes in shared templates like an onion, each team can own and maintain their part of the process. Combine this approach with the Git based workflows for managing changes to process templates, and using SemVer for quantifying the impact of changes to those templates, and you have a powerful combination of features that allow you to manage process templates at scale:</p>
<p><img src="/blog/img/platform_engineering_lunch_4/process-editor.png" alt="Publishing project process"></p>
<p>The onion model is just one example of how process templates can be used. You are, of course, free to create process templates defining more general deployment processes that don’t necessarily fit the pre/post-hook model. The key point is that process templates provide a powerful mechanism for sharing and maintaining architectural decisions, and Platform Hub provides strong opinions to support platform teams in managing those architectural decisions at scale.</p>
<h2 id="what-just-happened">What just happened?</h2>
<p>At the end of this post, you have:</p>
<ul>
<li>Updated the project structure to support a shared responsibility model, with multiple teams owning different stages of the deployment process.</li>
</ul>
<h2 id="whats-next">What’s next?</h2>
<p>You are 80% done with the series. In the <a href="/blog/platform-engineering-lunch-5">next and final post</a>, you’ll explore policies, which provide guardrails for projects to ensure they are compliant with organizational standards.</p>]]></content>
    </entry>
</feed>