all posts
2025-12-22·5 min read

GitHub: Executing Scheduled Actions (Cron)

CronGitHubScriptsWordPress

1. Summary

Context: The default WP Cron can be unreliable and not suitable for all actions. We need to execute the server site command where standard Unix cron is not available
Solution: We use GitHub Actions to SSH into the server and execute commands using a “Linear Chain” method. This eliminates the need for external intermediary servers.

2. Prerequisites

  • GitHub Access: Admin or Maintainer permissions.
  • WP Engine Access: Ability to manage SSH keys in the User Portal.
  • Local Terminal: To generate SSH keys (Mac/Linux preferred).

3. Technical Explanation: “The Chain Rule”

To ensure stability in WP Engine’s restricted shell, the code must be written using the Chain Rule.

The shell will disconnect if we attempt to send complex, multi-line logic (such as if statements or loops) over a non-interactive SSH connection. To bypass this, we format our script as a single, continuous stream:

  1. \ (Backslash): Tells the YAML file “This command continues on the next line” (for readability).
  2. && (AND Operator): Tells the Server “Run this command, and ONLY if it succeeds, run the next one.”

Crucial Constraints:

  • Do not place comments (#) inside the command chain block.
  • Do not use double quotes inside double quotes (e.g., for date formatting). Use single quotes ' for inner variables.

You can still use if and loops, but they must be formatted as One-Liners using semicolons (;).

  • Format: for x in list; do command; done

❌ This will FAIL (Multi-line):

# This breaks because the SSH stream loses the line breaks
for i in {1..5}
do
  echo "Number $i"
done

✅ This will WORK (One-line with Semicolons):

# Semicolons tell the server exactly where instructions end
for i in {1..5}; do echo "Number $i"; done

For complex logic that cannot fit on one line, use echo commands to generate a .sh file on the server first, then execute that file.

4. Setup Instructions

Step 1: Generate SSH Keys

Run this in your local terminal:

ssh-keygen -t rsa -b 4096 -C "github-actions-wpe" -f wpe_gh_key

(When asked for a passphrase, press Enter twice to leave it empty).

Step 2: Configure WP Engine

  1. Log in to WP Engine User Portal.
  2. Go to Users > SSH Keys.
  3. Add the contents of the Public Key (wpe_gh_key.pub).

Step 3: Configure GitHub Secrets

Go to Repo Settings > Security > Secrets and variables > Actions and add these three secrets:

Secret NameValue
WPE_SSH_KEYContent of wpe_gh_key (Private Key).
WPE_SSH_USERThe install name (e.g., punkcake).
WPE_SSH_HOSTThe SSH host (e.g., punkcake.ssh.wpengine.net).

Step 4: Create the Workflow File

Create a new file at .github/workflows/wpe-cron.yml.

name: WP Engine Cron Demo
on:
  schedule:
    # Schedule: Runs at 5:00 AM UTC every day
    - cron: '0 5 * * *'
  # Trigger: Allows manual execution via the "Actions" tab
  workflow_dispatch:

jobs:
  execute-cron:
    runs-on: ubuntu-latest
    steps:
      - name: SSH and Execute
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.WPE_SSH_HOST }}
          username: ${{ secrets.WPE_SSH_USER }}
          key: ${{ secrets.WPE_SSH_KEY }}
          port: 22
          # THE LINEAR CHAIN SCRIPT
          # Note: Date format uses single quotes '' to prevent syntax errors
          script: |
            echo "--- START ---" && \
            cd /nas/content/live/${{ secrets.WPE_SSH_USER }} && \
            echo "✅ Navigated to $(pwd)" && \
            echo "$(date '+%H:%M:%S %d:%m:%Y') - Cron executed successfully via GitHub Actions" >> gh-cron-test.txt && \
            echo "✅ Log updated" && \
            echo "--- CURRENT LOG CONTENT ---" && \
            tail -n 5 gh-cron-test.txt && \
            echo "--- DONE ---"

5. Expected Output

When you run this workflow, it will:

  1. Connect to the server.
  2. Navigate to the site root (ensuring we are not in the generic user home).
  3. Create gh-cron-test.txt if it doesn’t exist, or append to it if it does.
  4. Print the result for verification.

Example Log Output:

--- START ---
✅ Navigated to /nas/content/live/punkcake
✅ Log updated
--- CURRENT LOG CONTENT ---
16:20:01 22:12:2025 - Cron executed successfully via GitHub Actions
--- DONE ---

6. How to Test the Workflow

Since the workflow is configured with workflow_dispatch, you can trigger it manually at any time without waiting for the scheduled 5:00 AM run.

Step A: Trigger the Run

  1. Navigate to your GitHub Repository.
  2. Click the Actions tab (top menu).
  3. On the left sidebar, click the workflow name: WP Engine Cron Demo.
  4. You will see a blue banner on the right. Click the Run workflow dropdown button.
  5. Select the Branch: main and click the green Run workflow button.

Step B: Verify in GitHub

  1. Wait approx. 30 seconds for the run to complete.
  2. Success: The circle icon turns Green.
  3. Failure: The circle icon turns Red.
  4. Check the Output:
    • Click on the workflow run (e.g., “WP Engine Cron Demo #1”).
    • Click on the job box named execute-cron.
    • Click the arrow next to SSH and Execute to expand the logs.
    • You should see the custom output verifying the log update:

Step C: Verify on Server

To confirm the file is persisting correctly:

  1. Log in to your WP Engine environment via SFTP or SSH.
  2. Navigate to the site root: /nas/content/live/punkcake/
  3. Look for the file: gh-cron-test.txt.
  4. Open it to see the log history.
    • Note: Every time you click “Run workflow” in GitHub, a new line with a new timestamp will be added to this file.

7. Troubleshooting Common Errors

Error MessageMeaningSolution
Process exited with status 1The script failed at a specific step.Check the logs. If it failed at cd, your WPE_SSH_USER secret might be wrong.
extra operand / syntax errorQuote mismatch.Ensure you are using single quotes ' for the date command format, not double quotes.
tail: cannot open... No such fileFile creation failed.Ensure you are navigating to the correct folder (/nas/content/live/...) before trying to write the file.
Handshake failedSSH Key rejected.Re-add the Public Key to the WP Engine User Portal.

Found this useful?

There's more where that came from — explore the rest of the documentation, experiments, and production work.

Back to the index