<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Md. Asaduzzaman</title>
    <description>The latest articles on DEV Community by Md. Asaduzzaman (@s1kopath).</description>
    <link>https://dev.to/s1kopath</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4143852%2F3338fe3b-ccec-44c7-8a1c-02dc5dfdd8b2.jpg</url>
      <title>DEV Community: Md. Asaduzzaman</title>
      <link>https://dev.to/s1kopath</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/s1kopath"/>
    <language>en</language>
    <item>
      <title>Deploying to Cloudways From GitHub Actions Using an Access Token</title>
      <dc:creator>Md. Asaduzzaman</dc:creator>
      <pubDate>Sat, 26 Sep 2026 07:11:36 +0000</pubDate>
      <link>https://dev.to/s1kopath/deploying-to-cloudways-from-github-actions-using-an-access-token-3jfa</link>
      <guid>https://dev.to/s1kopath/deploying-to-cloudways-from-github-actions-using-an-access-token-3jfa</guid>
      <description>&lt;p&gt;Cloudways supports triggering a Git deployment directly from the API. Combined with GitHub Actions, this lets you deploy a Laravel (or any Git-based) application to Cloudways automatically on every push — authenticated with an &lt;strong&gt;Access Token&lt;/strong&gt; rather than the legacy API Key.&lt;/p&gt;

&lt;p&gt;This post walks through the CI/CD deployment step itself: the workflow, the API call, the parameters, and how to troubleshoot it.&lt;/p&gt;




&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub
   │
   │ push to develop
   ▼
GitHub Actions
   │
   │ Access Token (Bearer)
   ▼
Cloudways API
   │
   │ POST /git/pull
   ▼
Cloudways Application
   │
   │ SSH (optional, for post-deploy commands)
   ▼
Laravel deployment commands
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The deployment step calls Cloudways' Git deployment endpoint directly with &lt;code&gt;curl&lt;/code&gt;, authenticating with a Bearer token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /api/v1/git/pull
Authorization: Bearer YOUR_ACCESS_TOKEN
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 1 — Create an Access Token
&lt;/h2&gt;

&lt;p&gt;In the Cloudways Platform, go to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Profile → API Integration → Access Token&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a token dedicated to this deployment, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name:
My App - GitHub Actions Deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When choosing permissions, prefer &lt;strong&gt;Limited Access&lt;/strong&gt; scoped to the Git deployment operation rather than &lt;strong&gt;Full Access&lt;/strong&gt; — this limits the blast radius if the token is ever leaked.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — Store the Token as a GitHub Secret
&lt;/h2&gt;

&lt;p&gt;Never hard-code the token in your workflow file. Add it as a repository secret instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CW_ACCESS_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Along with the other values the deployment needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CW_ACCESS_TOKEN
CW_SERVER_ID
CW_APP_ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3 — The Deployment Step
&lt;/h2&gt;

&lt;p&gt;This is the core of the CI/CD job — a single &lt;code&gt;curl&lt;/code&gt; call that tells Cloudways to pull the latest code for a given app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cloudways Deployment&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;curl --fail-with-body --request POST \&lt;/span&gt;
      &lt;span class="s"&gt;--url https://api.cloudways.com/api/v1/git/pull \&lt;/span&gt;
      &lt;span class="s"&gt;--header "Authorization: Bearer ${{ secrets.CW_ACCESS_TOKEN }}" \&lt;/span&gt;
      &lt;span class="s"&gt;--header "Content-Type: application/x-www-form-urlencoded" \&lt;/span&gt;
      &lt;span class="s"&gt;--data-urlencode "server_id=${{ secrets.CW_SERVER_ID }}" \&lt;/span&gt;
      &lt;span class="s"&gt;--data-urlencode "app_id=${{ secrets.CW_APP_ID }}" \&lt;/span&gt;
      &lt;span class="s"&gt;--data-urlencode "branch_name=develop" \&lt;/span&gt;
      &lt;span class="s"&gt;--data-urlencode "deploy_path="&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Parameter Reference
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Authorization: Bearer ...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The Access Token, used as a Bearer token instead of the old email + API key pair&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;server_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Identifies the Cloudways server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;app_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Identifies the application to deploy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;branch_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The Git branch to pull (e.g. &lt;code&gt;develop&lt;/code&gt;, &lt;code&gt;main&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deploy_path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Optional; leave empty if the app is already configured with its deploy directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If your Cloudways app isn't already linked to a Git repository, you may also need to pass &lt;code&gt;git_url&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;--data-urlencode "git_url=${{ secrets.CW_GIT_URL }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4 — Full Workflow Example
&lt;/h2&gt;

&lt;p&gt;A minimal GitHub Actions workflow that deploys on push to &lt;code&gt;develop&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to Cloudways&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;develop"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cloudways Deployment&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;curl --fail-with-body --request POST \&lt;/span&gt;
            &lt;span class="s"&gt;--url https://api.cloudways.com/api/v1/git/pull \&lt;/span&gt;
            &lt;span class="s"&gt;--header "Authorization: Bearer ${{ secrets.CW_ACCESS_TOKEN }}" \&lt;/span&gt;
            &lt;span class="s"&gt;--header "Content-Type: application/x-www-form-urlencoded" \&lt;/span&gt;
            &lt;span class="s"&gt;--data-urlencode "server_id=${{ secrets.CW_SERVER_ID }}" \&lt;/span&gt;
            &lt;span class="s"&gt;--data-urlencode "app_id=${{ secrets.CW_APP_ID }}" \&lt;/span&gt;
            &lt;span class="s"&gt;--data-urlencode "branch_name=develop" \&lt;/span&gt;
            &lt;span class="s"&gt;--data-urlencode "deploy_path="&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your app needs post-deploy commands (migrations, cache clearing, etc.), add an SSH step after this one — that part is unaffected by the Access Token change, since it uses separate SSH credentials.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Call the API Directly Instead of Using a Third-Party Action?
&lt;/h2&gt;

&lt;p&gt;Several popular Cloudways GitHub Actions still expect the legacy &lt;code&gt;email&lt;/code&gt; + &lt;code&gt;api-key&lt;/code&gt; inputs. Rather than waiting for those actions to add Access Token support, calling &lt;code&gt;curl&lt;/code&gt; directly against the Cloudways API gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Immediate compatibility with Access Tokens&lt;/li&gt;
&lt;li&gt;Full control over every request parameter&lt;/li&gt;
&lt;li&gt;No dependency on a third-party action's release schedule&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub-hosted Ubuntu runners already include &lt;code&gt;curl&lt;/code&gt;, so no extra setup is needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;401 Unauthorized&lt;/strong&gt;&lt;br&gt;
Check the Access Token value, the GitHub Secret name, whether the token has expired, and that the header is written exactly as &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;403 Forbidden&lt;/strong&gt;&lt;br&gt;
Usually a permissions issue. Confirm the Access Token's Limited Access configuration includes the Git deployment operation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;400 Bad Request&lt;/strong&gt;&lt;br&gt;
Double-check &lt;code&gt;server_id&lt;/code&gt;, &lt;code&gt;app_id&lt;/code&gt;, &lt;code&gt;branch_name&lt;/code&gt;, and &lt;code&gt;deploy_path&lt;/code&gt;. If Cloudways requires a repository URL for your app, add &lt;code&gt;git_url&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Cloudways also provides an &lt;strong&gt;API Playground&lt;/strong&gt; where you can authorize with an Access Token and test the &lt;code&gt;/git/pull&lt;/code&gt; request independently of GitHub Actions, which is often faster for isolating the problem.&lt;/p&gt;


&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;A Cloudways CI/CD deployment with an Access Token comes down to one API call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/v1/git/pull
Authorization: Bearer &amp;lt;Access Token&amp;gt;
Params: server_id, app_id, branch_name, deploy_path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wrap that in a &lt;code&gt;curl&lt;/code&gt; step in your GitHub Actions workflow, store the token as a secret, and scope it to Limited Access — and your deployment pipeline is fully migrated off the legacy API Key model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Official Cloudways documentation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.cloudways.com/en/articles/5136065-how-to-create-and-manage-cloudways-api-access-tokens" rel="noopener noreferrer"&gt;Cloudways API Access Tokens&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.cloudways.com/en/articles/5124785-how-to-automatically-deploy-from-git-to-cloudways-using-webhooks" rel="noopener noreferrer"&gt;Cloudways Git deployment using the API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>cloudways</category>
    </item>
    <item>
      <title>Migrating Cloudways GitHub Actions CI/CD from API Key to Access Token</title>
      <dc:creator>Md. Asaduzzaman</dc:creator>
      <pubDate>Sat, 26 Sep 2026 07:04:16 +0000</pubDate>
      <link>https://dev.to/s1kopath/migrating-cloudways-github-actions-cicd-from-api-key-to-access-token-47g4</link>
      <guid>https://dev.to/s1kopath/migrating-cloudways-github-actions-cicd-from-api-key-to-access-token-47g4</guid>
      <description>&lt;p&gt;Cloudways is deprecating its legacy API Key authentication and moving integrations to &lt;strong&gt;API Access Tokens&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you have an existing Laravel application deployed through &lt;strong&gt;GitHub Actions → Cloudways&lt;/strong&gt;, this change may require updating your CI/CD workflow before the legacy API Key reaches its end of life.&lt;/p&gt;

&lt;p&gt;Cloudways currently lists &lt;strong&gt;October 15, 2026&lt;/strong&gt; as the end-of-life date for legacy API Keys. Existing integrations can continue using API Keys during the transition, but Cloudways recommends migrating active integrations to Access Tokens before the retirement date.&lt;/p&gt;

&lt;p&gt;This article explains how we migrated a Laravel + GitHub Actions + Cloudways deployment without changing the rest of our deployment process.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is a community-tested implementation based on Cloudways' documented Access Token API. It is not an official Cloudways guide.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Our Original Setup
&lt;/h2&gt;

&lt;p&gt;Our Laravel application was deployed using this flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub
   │
   │ push to develop
   ▼
GitHub Actions
   │
   │ Cloudways API Key
   ▼
Cloudways
   │
   │ Git pull
   ▼
Application
   │
   │ SSH
   ▼
Laravel deployment commands
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The GitHub Actions workflow originally used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cloudways Deployment&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;roelmagdaleno/cloudways-api-git-pull-action@stable&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_EMAIL }}&lt;/span&gt;
    &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_API_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;server-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_SERVER_ID }}&lt;/span&gt;
    &lt;span class="na"&gt;app-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_APP_ID }}&lt;/span&gt;
    &lt;span class="na"&gt;branch-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;develop"&lt;/span&gt;
    &lt;span class="na"&gt;deploy-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part here is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_API_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the credential that needs to be migrated.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Do We Need to Change It?
&lt;/h2&gt;

&lt;p&gt;Cloudways has introduced &lt;strong&gt;API Access Tokens&lt;/strong&gt; as the replacement for the legacy API Key.&lt;/p&gt;

&lt;p&gt;According to Cloudways, Access Tokens provide several advantages over the old API Key:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separate tokens can be created for different integrations.&lt;/li&gt;
&lt;li&gt;Tokens can have different permissions.&lt;/li&gt;
&lt;li&gt;Tokens can have expiration periods.&lt;/li&gt;
&lt;li&gt;Tokens can be revoked independently.&lt;/li&gt;
&lt;li&gt;Limited Access can be used to follow the principle of least privilege.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cloudways currently recommends &lt;strong&gt;Limited Access&lt;/strong&gt; when an integration only needs selected API operations.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem With Our Existing GitHub Action
&lt;/h2&gt;

&lt;p&gt;Our existing deployment action expects the old authentication model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;server-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;app-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cloudways' newer API authentication uses an Access Token directly as a Bearer token.&lt;/p&gt;

&lt;p&gt;The relevant request looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /api/v1/git/pull
Authorization: Bearer YOUR_ACCESS_TOKEN
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cloudways documents the Git deployment API using the &lt;code&gt;/git/pull&lt;/code&gt; endpoint and Access Token authentication.&lt;/p&gt;

&lt;p&gt;Rather than waiting for our existing third-party GitHub Action to change its authentication interface, we decided to call the Cloudways API directly from GitHub Actions using &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This keeps the deployment architecture almost exactly the same.&lt;/p&gt;




&lt;h2&gt;
  
  
  The New Deployment Flow
&lt;/h2&gt;

&lt;p&gt;After the migration, our flow became:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub
   │
   │ push to develop
   ▼
GitHub Actions
   │
   │ Cloudways Access Token
   ▼
Cloudways API
   │
   │ POST /git/pull
   ▼
Cloudways Application
   │
   │ SSH
   ▼
Laravel deployment commands
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important difference is simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OLD:

Email + API Key
       ↓
Cloudways API


NEW:

Access Token
       ↓
Cloudways API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SSH part of the deployment remains independent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1 — Create a Cloudways Access Token
&lt;/h2&gt;

&lt;p&gt;Go to your Cloudways Platform and open:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Profile → API Integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cloudways provides an &lt;strong&gt;Access Token&lt;/strong&gt; section where you can create and manage tokens.&lt;/p&gt;

&lt;p&gt;Create a token specifically for your GitHub Actions deployment.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name:
Promise Assets - GitHub Actions Staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using a descriptive name is useful because Cloudways allows multiple Access Tokens for different integrations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — Choose the Appropriate Permission
&lt;/h2&gt;

&lt;p&gt;For a deployment integration, you generally don't need unrestricted access to your entire Cloudways account.&lt;/p&gt;

&lt;p&gt;Cloudways provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limited Access&lt;/li&gt;
&lt;li&gt;Read-Only Access&lt;/li&gt;
&lt;li&gt;Full Access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an integration that performs a Git deployment, &lt;strong&gt;Limited Access&lt;/strong&gt; is the appropriate approach when the required Git endpoint is available in the permission selector.&lt;/p&gt;

&lt;p&gt;Look for the Git-related deployment operation corresponding to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /git/pull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cloudways recommends Limited Access for integrations that only require specific API operations.&lt;/p&gt;

&lt;p&gt;Avoid giving Full Access unless your integration actually requires it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — Store the Token in GitHub Secrets
&lt;/h2&gt;

&lt;p&gt;Do not put the Access Token directly into your YAML file.&lt;/p&gt;

&lt;p&gt;Instead, create a GitHub repository secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CW_DEV_ACCESS_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your deployment-related secrets can then look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CW_DEV_ACCESS_TOKEN
CW_DEV_SERVER_ID
CW_DEV_APP_ID
CW_DEV_HOST
CW_DEV_SSH_USER
CW_DEV_SSH_PASS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cloudways explicitly recommends treating Access Tokens as sensitive credentials and not publishing them in source code, screenshots, public repositories, or other publicly accessible locations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4 — Replace the Old Cloudways Deployment Action
&lt;/h2&gt;

&lt;p&gt;Previously, we had:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cloudways Deployment&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;roelmagdaleno/cloudways-api-git-pull-action@stable&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_EMAIL }}&lt;/span&gt;
    &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_API_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;server-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_SERVER_ID }}&lt;/span&gt;
    &lt;span class="na"&gt;app-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_APP_ID }}&lt;/span&gt;
    &lt;span class="na"&gt;branch-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;develop"&lt;/span&gt;
    &lt;span class="na"&gt;deploy-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We replaced that with a direct API request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cloudways Deployment&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;curl --fail-with-body --request POST \&lt;/span&gt;
      &lt;span class="s"&gt;--url https://api.cloudways.com/api/v1/git/pull \&lt;/span&gt;
      &lt;span class="s"&gt;--header "Authorization: Bearer ${{ secrets.CW_DEV_ACCESS_TOKEN }}" \&lt;/span&gt;
      &lt;span class="s"&gt;--header "Content-Type: application/x-www-form-urlencoded" \&lt;/span&gt;
      &lt;span class="s"&gt;--data-urlencode "server_id=${{ secrets.CW_DEV_SERVER_ID }}" \&lt;/span&gt;
      &lt;span class="s"&gt;--data-urlencode "app_id=${{ secrets.CW_DEV_APP_ID }}" \&lt;/span&gt;
      &lt;span class="s"&gt;--data-urlencode "branch_name=develop" \&lt;/span&gt;
      &lt;span class="s"&gt;--data-urlencode "deploy_path="&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the key change in the migration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Are We Using &lt;code&gt;curl&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;GitHub-hosted Ubuntu runners already have &lt;code&gt;curl&lt;/code&gt; available.&lt;/p&gt;

&lt;p&gt;Instead of relying on a third-party GitHub Action to handle Cloudways authentication, we're making the API request ourselves.&lt;/p&gt;

&lt;p&gt;This gives us direct control over:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authorization
Server ID
Application ID
Branch
Deploy path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also means that the CI/CD workflow doesn't depend on whether the third-party action has already implemented Cloudways' new Access Token authentication.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Does Each Parameter Do?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Access Token
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;--header "Authorization&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bearer ${{ secrets.CW_DEV_ACCESS_TOKEN }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This replaces the old:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_API_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cloudways' newer authentication model uses the Access Token as a Bearer token.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server ID
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;--data-urlencode "server_id=${{ secrets.CW_DEV_SERVER_ID }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This identifies the Cloudways server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Application ID
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;--data-urlencode "app_id=${{ secrets.CW_DEV_APP_ID }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This identifies the application that should receive the deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;--data-urlencode "branch_name=develop"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our staging environment deploys the &lt;code&gt;develop&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;Change this if your workflow uses another branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy Path
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;--data-urlencode "deploy_path="&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We leave the deploy path empty because the Cloudways application is already configured with its deployment directory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Complete GitHub Actions Workflow
&lt;/h2&gt;

&lt;p&gt;Here is the complete workflow used for our Laravel staging deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Staging Deployment&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;develop"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;develop"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;closed&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FORCE_JAVASCRIPT_ACTIONS_TO_NODE24&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cloudways Deployment&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;curl --fail-with-body --request POST \&lt;/span&gt;
            &lt;span class="s"&gt;--url https://api.cloudways.com/api/v1/git/pull \&lt;/span&gt;
            &lt;span class="s"&gt;--header "Authorization: Bearer ${{ secrets.CW_DEV_ACCESS_TOKEN }}" \&lt;/span&gt;
            &lt;span class="s"&gt;--header "Content-Type: application/x-www-form-urlencoded" \&lt;/span&gt;
            &lt;span class="s"&gt;--data-urlencode "server_id=${{ secrets.CW_DEV_SERVER_ID }}" \&lt;/span&gt;
            &lt;span class="s"&gt;--data-urlencode "app_id=${{ secrets.CW_DEV_APP_ID }}" \&lt;/span&gt;
            &lt;span class="s"&gt;--data-urlencode "branch_name=develop" \&lt;/span&gt;
            &lt;span class="s"&gt;--data-urlencode "deploy_path="&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Executing Linux Commands&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appleboy/ssh-action@v1.0.3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_HOST }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_SSH_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;22&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_SSH_PASS }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;# Navigate to Promise Assets application directory&lt;/span&gt;
            &lt;span class="s"&gt;for d in $(find $HOME /home -type d -name "public_html" 2&amp;gt;/dev/null); do&lt;/span&gt;
                &lt;span class="s"&gt;if [ -d "$d/Modules/CRM" ] || [ -f "$d/app/Console/Commands/SyncPermissionsCommand.php" ]; then&lt;/span&gt;
                    &lt;span class="s"&gt;cd "$d"&lt;/span&gt;
                    &lt;span class="s"&gt;break&lt;/span&gt;
                &lt;span class="s"&gt;fi&lt;/span&gt;
            &lt;span class="s"&gt;done&lt;/span&gt;

            &lt;span class="s"&gt;echo "Deploying in: $(pwd)"&lt;/span&gt;

            &lt;span class="s"&gt;if [ ! -f "artisan" ]; then&lt;/span&gt;
                &lt;span class="s"&gt;echo "ERROR: Could not locate promise-assets application directory!"&lt;/span&gt;
                &lt;span class="s"&gt;exit 1&lt;/span&gt;
            &lt;span class="s"&gt;fi&lt;/span&gt;

            &lt;span class="s"&gt;echo "executing composer install...."&lt;/span&gt;
            &lt;span class="s"&gt;composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist&lt;/span&gt;

            &lt;span class="s"&gt;echo "clearing old cache and discovering packages...."&lt;/span&gt;
            &lt;span class="s"&gt;php ./artisan optimize:clear&lt;/span&gt;
            &lt;span class="s"&gt;php ./artisan package:discover --ansi&lt;/span&gt;

            &lt;span class="s"&gt;echo "running migration...."&lt;/span&gt;
            &lt;span class="s"&gt;php artisan migrate --force&lt;/span&gt;

            &lt;span class="s"&gt;# Sync permissions&lt;/span&gt;
            &lt;span class="s"&gt;echo "syncing permissions..."&lt;/span&gt;
            &lt;span class="s"&gt;php artisan permission:sync 2&amp;gt;/dev/null || true&lt;/span&gt;

            &lt;span class="s"&gt;echo "clearing cache...."&lt;/span&gt;
            &lt;span class="s"&gt;php ./artisan optimize:clear&lt;/span&gt;

            &lt;span class="s"&gt;echo "done...."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What Changed From the Old Workflow?
&lt;/h2&gt;

&lt;p&gt;The rest of the workflow stays almost exactly the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;roelmagdaleno/cloudways-api-git-pull-action@stable&lt;/span&gt;

&lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_EMAIL }}&lt;/span&gt;
  &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_API_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
  &lt;span class="s"&gt;curl --request POST \&lt;/span&gt;
    &lt;span class="s"&gt;--url https://api.cloudways.com/api/v1/git/pull \&lt;/span&gt;
    &lt;span class="s"&gt;--header "Authorization: Bearer ${{ secrets.CW_DEV_ACCESS_TOKEN }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SSH deployment remains unchanged.&lt;/p&gt;

&lt;p&gt;This is useful because your Laravel deployment commands don't need to know anything about the Cloudways API authentication change.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Didn't We Add &lt;code&gt;git_url&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Cloudways' current Git deployment documentation includes &lt;code&gt;git_url&lt;/code&gt; in its API example.&lt;/p&gt;

&lt;p&gt;However, in our existing setup the Cloudways application was already configured with its Git repository, and our previous deployment workflow successfully triggered a Git pull without explicitly providing the repository URL.&lt;/p&gt;

&lt;p&gt;Therefore, our migration initially kept the existing application configuration and changed only the authentication mechanism.&lt;/p&gt;

&lt;p&gt;The practical lesson is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't unnecessarily change a working deployment configuration while migrating authentication.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If your Cloudways API response indicates that &lt;code&gt;git_url&lt;/code&gt; is required for your particular application/API configuration, add it to the request.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;--data-urlencode "git_url=${{ secrets.CW_DEV_GIT_URL }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository URL should then be stored as a GitHub Secret rather than hard-coded if you prefer to keep your deployment configuration centralized.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing the Migration
&lt;/h2&gt;

&lt;p&gt;Before deleting the old API Key, test the new workflow.&lt;/p&gt;

&lt;p&gt;A safe migration sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Create Access Token
        ↓
2. Configure GitHub Secret
        ↓
3. Update GitHub Actions
        ↓
4. Push to develop
        ↓
5. Verify Cloudways Git deployment
        ↓
6. Verify SSH deployment
        ↓
7. Test another deployment
        ↓
8. Remove old API Key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't immediately delete the old credential.&lt;/p&gt;

&lt;p&gt;Keeping it temporarily gives you a fallback while validating the new authentication.&lt;/p&gt;




&lt;h2&gt;
  
  
  Useful Error Checks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  401 Unauthorized
&lt;/h3&gt;

&lt;p&gt;If you receive something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;401 Unauthorized
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access Token value&lt;/li&gt;
&lt;li&gt;GitHub Secret name&lt;/li&gt;
&lt;li&gt;Token expiration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Authorization: Bearer&lt;/code&gt; syntax&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make sure the secret is referenced exactly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;${{ secrets.CW_DEV_ACCESS_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  403 Forbidden
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;403&lt;/code&gt; generally indicates an authorization/permission problem.&lt;/p&gt;

&lt;p&gt;Check the Access Token's Limited Access configuration and make sure it has permission for the Git deployment operation required by your workflow.&lt;/p&gt;

&lt;p&gt;Cloudways Access Tokens can be configured with specific API endpoint permissions.&lt;/p&gt;




&lt;h3&gt;
  
  
  400 Bad Request
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;400&lt;/code&gt; usually means that one or more parameters aren't acceptable.&lt;/p&gt;

&lt;p&gt;Check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server_id
app_id
branch_name
deploy_path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your Cloudways configuration/API response requires the repository URL, also provide:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git_url
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cloudways provides an API Playground that can be useful for testing API requests independently from GitHub Actions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing With the Cloudways API Playground
&lt;/h2&gt;

&lt;p&gt;Cloudways provides an API Playground where you can authorize with an Access Token and test API endpoints.&lt;/p&gt;

&lt;p&gt;This can be useful before troubleshooting GitHub Actions.&lt;/p&gt;

&lt;p&gt;The basic process is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the Cloudways API Playground.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Authorize&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Access Token&lt;/strong&gt; authentication option.&lt;/li&gt;
&lt;li&gt;Enter your Access Token.&lt;/li&gt;
&lt;li&gt;Select the relevant Git endpoint.&lt;/li&gt;
&lt;li&gt;Provide the required parameters.&lt;/li&gt;
&lt;li&gt;Execute the request.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cloudways notes that actions performed through the API Playground operate against the authenticated account, so use care when testing endpoints that modify infrastructure or applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  Don't Confuse API Authentication With SSH Authentication
&lt;/h2&gt;

&lt;p&gt;One thing that initially made this migration confusing for us was that the deployment has &lt;strong&gt;two separate authentication mechanisms&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cloudways API
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub Actions
       │
       │ Access Token
       ▼
Cloudways API
       │
       ▼
Git Pull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SSH
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub Actions
       │
       │ SSH credentials
       ▼
Cloudways Server
       │
       ▼
Laravel commands
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Cloudways Access Token replaces the &lt;strong&gt;API Key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It does not replace your SSH credentials.&lt;/p&gt;

&lt;p&gt;Therefore, this can remain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appleboy/ssh-action@v1.0.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with your existing SSH configuration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Our Laravel Deployment Commands
&lt;/h2&gt;

&lt;p&gt;Once Cloudways has pulled the latest code, our workflow connects over SSH and performs the Laravel deployment tasks.&lt;/p&gt;

&lt;p&gt;The commands include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;followed by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan optimize:clear
php artisan package:discover &lt;span class="nt"&gt;--ansi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then migrations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan migrate &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then our application-specific permission synchronization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan permission:sync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan optimize:clear
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This part of the deployment does not depend on the Cloudways API Key.&lt;/p&gt;




&lt;h2&gt;
  
  
  GitHub Secrets After Migration
&lt;/h2&gt;

&lt;p&gt;Our new secrets look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CW_DEV_ACCESS_TOKEN
CW_DEV_SERVER_ID
CW_DEV_APP_ID
CW_DEV_HOST
CW_DEV_SSH_USER
CW_DEV_SSH_PASS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The old credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CW_DEV_EMAIL
CW_DEV_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;are no longer required by the new Cloudways API call.&lt;/p&gt;

&lt;p&gt;We recommend keeping the old API Key temporarily during migration and removing it only after the new deployment has been successfully tested.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security Considerations
&lt;/h2&gt;

&lt;p&gt;An Access Token is effectively a credential for your Cloudways account/API.&lt;/p&gt;

&lt;p&gt;Never commit this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;CW_DEV_ACCESS_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;actual-token-here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to your repository.&lt;/p&gt;

&lt;p&gt;Instead use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;${{ secrets.CW_DEV_ACCESS_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cloudways explicitly advises users not to expose Access Tokens in public repositories, screenshots, support tickets, chats, or other publicly accessible locations.&lt;/p&gt;

&lt;p&gt;Also consider creating a dedicated token for each deployment environment.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Production:
Cloudways - GitHub Actions - Production

Staging:
Cloudways - GitHub Actions - Staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes revocation and troubleshooting much easier.&lt;/p&gt;

&lt;p&gt;Cloudways supports multiple Access Tokens, with separate permissions and expiration settings.&lt;/p&gt;




&lt;h2&gt;
  
  
  One More Improvement: Don't Use Full Access Unless You Need It
&lt;/h2&gt;

&lt;p&gt;When creating the token, you may see options such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Limited Access
Read-Only Access
Full Access
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a deployment integration, don't automatically choose Full Access.&lt;/p&gt;

&lt;p&gt;If the Git deployment endpoint is available under Limited Access, select only the required Git operation.&lt;/p&gt;

&lt;p&gt;This follows the principle of least privilege and limits what a compromised CI/CD credential can do. Cloudways itself recommends Limited Access for integrations that only require selected API operations.&lt;/p&gt;




&lt;h2&gt;
  
  
  What About the Old API Key?
&lt;/h2&gt;

&lt;p&gt;Cloudways currently states that the legacy API Key is scheduled for end of life on:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;October 15, 2026&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Existing users can continue using the old API Key during the transition period, but Cloudways recommends migrating active integrations before the retirement date. After the deprecation period, legacy API Keys will be revoked and integrations that still depend on them may stop working.&lt;/p&gt;

&lt;p&gt;So this isn't just a cosmetic change.&lt;/p&gt;

&lt;p&gt;If your CI/CD pipeline still contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CW_DEV_API_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it's worth identifying and migrating it before the deadline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Architecture
&lt;/h2&gt;

&lt;p&gt;After the migration, our deployment architecture looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                     GitHub
                        │
                        │ push
                        ▼
                GitHub Actions
                        │
                        │ Access Token
                        ▼
              Cloudways API
                        │
                        │ POST /git/pull
                        ▼
              Cloudways Application
                        │
                        │ latest code
                        ▼
                 SSH Connection
                        │
          ┌─────────────┼──────────────┐
          │             │              │
          ▼             ▼              ▼
      Composer       Migrations    Permissions
          │             │              │
          └─────────────┼──────────────┘
                        │
                        ▼
                  Cache Clear
                        │
                        ▼
                    Deployed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main takeaway is that &lt;strong&gt;you don't necessarily need to redesign your entire CI/CD pipeline just because Cloudways is retiring the legacy API Key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If your existing Cloudways application is already configured for Git deployment, you can keep the surrounding deployment process and replace the legacy API authentication with an Access Token-based &lt;code&gt;/git/pull&lt;/code&gt; request.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Cloudways' move from API Keys to Access Tokens requires existing CI/CD integrations to be reviewed before &lt;strong&gt;October 15, 2026&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For a Laravel application using GitHub Actions, one practical migration path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Legacy API Key
      ↓
Cloudways /git/pull
      ↓
Replace with
      ↓
Access Token + Bearer authentication
      ↓
Cloudways /git/pull
      ↓
Continue using SSH for Laravel deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our case, this allowed us to migrate the Cloudways authentication layer without changing the Composer, migration, permission-sync, or cache-clearing stages of the existing Laravel deployment.&lt;/p&gt;

&lt;p&gt;If you're currently using a third-party GitHub Action that only supports Cloudways' legacy API Key, a direct API call from GitHub Actions is one option worth considering while migrating to the new authentication system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Official Cloudways documentation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.cloudways.com/en/articles/5136065-how-to-create-and-manage-cloudways-api-access-tokens" rel="noopener noreferrer"&gt;Cloudways API Access Tokens&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.cloudways.com/en/articles/5124785-how-to-automatically-deploy-from-git-to-cloudways-using-webhooks" rel="noopener noreferrer"&gt;Cloudways Git deployment using the API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>automation</category>
      <category>architecture</category>
      <category>cloudways</category>
    </item>
  </channel>
</rss>
