Case Study

How to automate blog publishing with GitHub Actions

September 22, 2026 · 12 min read
← Back to blog

Scheduled publishing sounds simple: write the article now, choose a date, and let the system publish it later. On a static website there is no WordPress scheduler waiting behind the scenes, so you have to create that publishing system yourself.

WordRanger was where I learned the difference between having a scheduled workflow and having a reliable publishing process.

The key lesson: do not automate a publishing process you have never tested manually. The schedule should trigger a process you already know works.

What GitHub Actions is doing in this setup

GitHub Actions runs workflows defined in YAML files inside .github/workflows. A workflow can run after a repository event, on a manual command, or on a schedule.

For a small static blog, the publishing job can be very simple conceptually:

  1. Check today's date.
  2. Look for a matching dated folder.
  3. Move or copy the due article into the live blog directory.
  4. Remove the scheduled copy.
  5. Commit the change.
  6. Regenerate the sitemap or blog index if needed.
  7. Deploy the current production files.

A simple folder model

One understandable approach is to keep scheduled posts outside the public blog folder:

scheduled/
  2026-09-22/
    blog/
      automate-blog-publishing-github-actions.html
blog/
  already-published-article.html

The production build should exclude scheduled/. Otherwise the unpublished file may still be publicly accessible even though it is not linked from the blog page.

The schedule itself is only one part

A GitHub Actions schedule uses cron syntax. GitHub currently allows scheduled workflows on the default branch, and scheduled runs use the latest commit on that branch. GitHub also supports an IANA timezone field for timezone-aware scheduling.

A schedule might look conceptually like this:

on:
  workflow_dispatch:
  schedule:
    - cron: '15 9,10,11,12 * * *'
      timezone: "America/New_York"

The repeated hours can give a publishing job more than one opportunity to catch a due folder. The manual workflow_dispatch trigger is equally important because it gives you a way to test or recover without editing the article itself.

Why a scheduled run may not happen exactly when expected

This was one of the confusing WordRanger moments. A scheduled job did not appear when expected, even though the workflow could run manually.

GitHub's documentation says scheduled Actions can be delayed during periods of high load, especially near the start of an hour. If load is sufficiently high, some queued jobs can be dropped. GitHub specifically recommends scheduling at a different minute of the hour to reduce this risk.

That means a missed run does not automatically tell you what failed. You need to separate trigger problems from workflow-logic problems.

Use manual dispatch as your first test

Before trusting the scheduler:

  1. Create a harmless test article or dated folder.
  2. Run the workflow manually from the Actions tab.
  3. Confirm the article reaches the correct live path.
  4. Check the generated sitemap and blog index.
  5. Confirm the deployment completes.
  6. Open the live page.

If the manual run fails, fix the publishing logic first. If the manual run succeeds but the scheduled run does not appear, investigate the trigger and GitHub's scheduling behavior separately.

Do not let a scheduled folder overwrite the current site

One mistake that can hide inside a working scheduler is copying an entire dated folder over production. If the folder contains an old copy of blog.html or sitemap.xml, the scheduled release can quietly restore stale versions of those files.

A safer pattern is to store only the new article in the scheduled folder. On publish day, let the workflow update the current blog index and regenerate the sitemap from the current production content.

Risky patternSafer pattern
Store article + old blog index + old sitemap in each dated folderStore only the article and generate shared files at publish time
Copy the entire scheduled folder into productionCopy only approved article files
Assume a scheduled run will be exactAllow multiple check windows and retain manual dispatch
Publish before testing the workflowRun the full logic manually first

Protect the production branch

Scheduled workflows only run from the default branch, so the workflow file needs to exist there. For a small personal project, main often acts as production.

The workflow also needs only the permissions required for its job. If it commits published files back to the repository, it needs appropriate contents write permission. Deployment credentials such as Cloudflare API tokens should live in repository secrets rather than inside the YAML or public code.

Update dates when the article actually publishes

If an article was drafted weeks earlier, decide whether the visible publication date and structured-data datePublished should reflect the scheduled release. A workflow can normalize those fields when it publishes.

The important thing is consistency. The visible date, structured data, sitemap, and blog card should not contradict one another.

Regenerate the sitemap after publishing

A scheduled article should become discoverable through normal internal links and the sitemap once it is public. Generating the sitemap during deployment is safer than maintaining a series of future sitemap snapshots.

That also prevents unpublished URLs from being submitted to search engines before the article is live.

What to inspect when the workflow fails

  1. Did a workflow run exist? If no, investigate the trigger and schedule.
  2. Which job failed? Open the run and identify the first failed job.
  3. Which step failed? Read the step logs rather than guessing from the red X.
  4. Was the dated folder found? Check timezone and folder naming.
  5. Did Git have permission to commit? Review workflow permissions.
  6. Did deployment fail after publishing? The repository may have updated correctly even if hosting did not.

Do you still need a test scheduler?

Not forever. A dedicated test workflow is useful while you are proving that scheduled events and publishing logic behave as expected. Once the production workflow has run successfully on schedule several times and you have a manual fallback, a permanent test scheduler may only create noise.

The better long-term safeguard is observable production: clear workflow names, useful logs, manual dispatch, sensible commit messages, and notifications for failures.

A beginner-friendly scheduled publishing checklist

What WordRanger changed about how I think about automation

Before building the scheduler, automation sounded like a way to avoid work. After debugging it, I think of automation differently: it moves repeated work into a system, but that system becomes something you have to understand well enough to verify.

That is still a good trade. Once the process works, scheduling a post means creating the article and placing it in the right dated folder instead of remembering to publish manually. But the useful automation is the version with a clear fallback and no mystery about what happens when it fails.

For a first static site: automate one boring, repeatable task at a time. Keep the workflow small enough that you can read the logs and understand the path from draft to live page.

References

Build a site worth automating

Before creating publishing machinery, make sure the niche supports useful content you can maintain.

Validate a niche →