Site Build

Scheduling Future Posts in Astro

How this site now supports prewritten posts that stay hidden until their publish date, with GitHub Actions rebuilding the static site on a schedule.

3 min readSeries: Site Build
Blue-toned workspace representing an automated publishing pipeline

One of the first practical features I wanted for this site was the ability to write ahead.

A static site is great for durability: Markdown in Git, predictable builds, no database, no CMS dependency, and very little operational surface area. The tradeoff is that static HTML does not wake itself up at midnight and decide to publish something new. If a post is excluded during the build, it will stay excluded until the next build.

So I added a small scheduling layer.

The publishing rule

Posts and projects now pass through one shared visibility check:

export function isPublishedAt(entry: AnyEntry, now: Date) {
  return entry.data.published && !entry.data.draft && entry.data.date.valueOf() <= now.valueOf();
}

export function isPublished(entry: AnyEntry) {
  return isPublishedAt(entry, new Date());
}

That means an entry appears on the site only when all of these are true:

  • published is true.
  • draft is false.
  • date is less than or equal to the current build time.

The important bit is the date check. A post can exist in the repository with a future date, but it will not be included in the generated site until a build happens after that date.

What a scheduled post looks like

A future post is just normal Markdown frontmatter:

---
title: "Future post"
summary: "Short summary."
date: 2026-08-10
published: true
draft: false
tags: [Site Build]
category: Site Build
---

There is no special queue, database table, or publishing service. The content file itself carries the scheduling metadata.

That keeps the system simple enough that I can write a post by hand, generate one with an assistant, or transform structured notes into Markdown without needing a separate editorial backend.

Making static publishing automatic

Filtering future-dated posts is only half of the feature. The site also needs to rebuild regularly.

The Cloudflare Pages deployment workflow now includes a scheduled GitHub Actions trigger:

on:
  push:
    branches: [main]
  workflow_dispatch:
  schedule:
    # Rebuild periodically so future-dated posts become visible after their publish date.
    - cron: '17 * * * *'

That hourly rebuild is what turns a future-dated Markdown file into a published page after its date arrives.

Pushes still deploy immediately. Manual dispatch still works. The scheduled build just adds a low-friction publishing clock.

Verifying it

I tested the behavior with a temporary post dated far in the future:

---
title: "Future Schedule Smoke Test"
summary: "Temporary post used to verify future-dated content stays hidden."
date: 2099-01-01
published: true
draft: false
tags: [Site Build]
category: Site Build
---

Then I built the site and checked the generated dist output. The future post did not appear.

After removing the temporary file, the normal build still passed, and both GitHub workflows completed successfully:

  • CI
  • Cloudflare Pages deploy

A public repo caveat

There is one important distinction: hidden from the generated website does not mean private.

If this repository is public, a future-dated post committed to main is still visible to anyone browsing the repository. The scheduling logic only controls whether the post appears in the built website, RSS feed, tags, archives, and generated routes.

For non-sensitive drafts, that is fine. For embargoed or private writing, the right workflow is different:

  • keep the draft local,
  • use a private branch or private repo,
  • or merge/generate the Markdown at publish time.

The scheduler is for reducing publishing friction, not for access control.

Why this fits the site

This lab notebook is meant to accumulate work without ceremony. Future-dated posts make it easier to batch writing, prepare project notes in advance, and let routine site maintenance happen automatically.

It is still just Markdown, Git, Astro, GitHub Actions, and Cloudflare Pages.

That is the point.