Why CloudPanel Deployments Need Automation

CloudPanel is one of the best free server management panels available for self-hosted PHP applications. It provides a clean UI for managing sites, SSL certificates, databases, cron jobs, and vhosts without the complexity or cost of cPanel or Plesk. Many Laravel developers run their client sites on CloudPanel-managed VPS instances.

But CloudPanel has a deployment gap. Unlike platform-specific services such as Laravel Forge, Vapor, or Heroku, CloudPanel provides no native deployment pipeline. Releasing code to a CloudPanel server means:

  • SSH-ing in manually and running git pull && composer install && php artisan migrate
  • Writing a custom deploy script and triggering it via webhook or cron
  • Using a general-purpose deployment tool like Deployer.php and configuring it for CloudPanel specifics
  • Paying for a separate deployment service like Envoyer or Buddy.Works

CloudPanel Deploy Action takes a different approach: a purpose-built GitHub Action that knows about CloudPanel's specific deployment needs and handles them automatically as part of your CI/CD pipeline.

Prerequisites

Before setting up the action, you need:

  1. A CloudPanel-managed server with SSH access enabled
  2. A GitHub repository containing your application
  3. An SSH key pair — the private key will be stored as a GitHub Secret, the public key added to the server

Setting Up SSH Access

Generate a dedicated deployment key pair on your local machine:

ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/cloudpanel_deploy

Add the public key to your server's ~/.ssh/authorized_keys:

cat ~/.ssh/cloudpanel_deploy.pub | ssh user@your-server 'cat >> ~/.ssh/authorized_keys'

In your GitHub repository, go to Settings → Secrets and variables → Actions and add:

Secret Name Value
DEPLOY_HOST Your server's IP address or hostname
DEPLOY_USER SSH username (usually your CloudPanel site username)
DEPLOY_KEY Contents of ~/.ssh/cloudpanel_deploy (private key)
DEPLOY_PATH Absolute path to your application on the server

Basic Laravel Deployment Workflow

Create .github/workflows/deploy.yml in your repository:

name: Deploy to CloudPanel

on:
  push:
    branches:
      - main

jobs:
  deploy:
    name: Deploy to Production
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Run tests
        run: |
          composer install --no-interaction --prefer-dist
          cp .env.example .env
          php artisan key:generate
          php artisan test

      - name: Deploy to CloudPanel
        uses: amjadiqbal/cloudpanel-deploy-action@v1
        with:
          host: ${{ secrets.DEPLOY_HOST }}
          username: ${{ secrets.DEPLOY_USER }}
          ssh-private-key: ${{ secrets.DEPLOY_KEY }}
          deploy-path: ${{ secrets.DEPLOY_PATH }}
          app-type: laravel
          php-version: '8.3'

With this workflow, every push to main will:

  1. Run your test suite
  2. Deploy to the server only if all tests pass
  3. Execute the full Laravel deployment sequence automatically

What the Laravel Deployment Step Executes

For app-type: laravel, the action runs the following commands on the server:

cd /path/to/app

# Pull latest code
git pull origin main

# Install/update dependencies
composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev

# Run database migrations
php artisan migrate --force

# Clear and rebuild caches
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache

# Restart queue workers
php artisan queue:restart

# Reload PHP-FPM (CloudPanel-specific)
clpctl service:reload php-fpm

All of these steps are configurable. If you use a deployment script (./deploy.sh) instead of the default steps, set custom-script: true and the action will execute your script rather than the built-in sequence.

Node.js Application Deployment

For a Next.js or Node.js API on a CloudPanel server:

- name: Deploy to CloudPanel
  uses: amjadiqbal/cloudpanel-deploy-action@v1
  with:
    host: ${{ secrets.DEPLOY_HOST }}
    username: ${{ secrets.DEPLOY_USER }}
    ssh-private-key: ${{ secrets.DEPLOY_KEY }}
    deploy-path: ${{ secrets.DEPLOY_PATH }}
    app-type: nodejs
    node-version: '20'
    pm2-process: my-nextjs-app

For Node.js apps, the action runs npm ci && npm run build and then reloads the PM2 process.

Zero-Downtime Deployments

The default deployment sequence does not guarantee zero downtime — there is a window between git pull and php artisan migrate where the running code may not match the deployed database schema.

For zero-downtime deployments, use the maintenance-mode: true option:

with:
  maintenance-mode: true

This wraps the deployment in php artisan down and php artisan up calls. For applications that need true zero-downtime (no maintenance mode), configure a before-deploy hook to switch a symlink, following the atomic deployment pattern.

Deployment Notifications

The action can post deployment notifications to Slack or Discord:

- name: Deploy to CloudPanel
  uses: amjadiqbal/cloudpanel-deploy-action@v1
  with:
    host: ${{ secrets.DEPLOY_HOST }}
    username: ${{ secrets.DEPLOY_USER }}
    ssh-private-key: ${{ secrets.DEPLOY_KEY }}
    deploy-path: ${{ secrets.DEPLOY_PATH }}
    app-type: laravel
    notify-slack: ${{ secrets.SLACK_WEBHOOK_URL }}

GitHub Repository

Full configuration documentation, all available options, and example workflow files are on GitHub and available on the GitHub Actions Marketplace.

Conclusion

Professional CI/CD pipelines are not just for teams using managed hosting. If you run your own CloudPanel server — which many Laravel developers do — you deserve the same deployment automation that Forge or Vapor users take for granted. CloudPanel Deploy Action brings that automation to any CloudPanel server in under five minutes of configuration.

Gate your deployments behind automated test suites. Stop SSH-ing into servers to deploy code. Let GitHub Actions handle it.