The goal of this site is not to have an interesting infrastructure. The goal is to make publishing, changing a page, and receiving an email boring enough that I do not have to think about them.

The current setup is deliberately small:

  • Astro renders the site.
  • GitHub stores the code and content and keeps the history.
  • Cloudflare Pages builds and serves it.
  • Pages CMS gives me an editor for the content stored in GitHub.
  • Cloudflare Email Routing forwards mail sent to my domain into the inbox I already use.
  • Gmail is the inbox I actually read.
  • Resend handles outbound mail when the site, an automation, or Gmail needs an SMTP sender for my domain.

There is no separate content database I have to keep in sync and no mail server I have to administer.

The publishing path

The source of truth is the GitHub repository.

Pages CMS
    ↓ commit
GitHub
    ↓ deploy
Cloudflare Pages
    ↓
bemorin.com

Cloudflare Pages can connect directly to a Git repository and rebuild after a push. For a mostly static Astro site, requests for static assets are free and unlimited on both the Free and Paid plans; the Free plan currently allows up to 500 Pages builds per month. The domain itself is still a separate cost.

That gives me a useful separation: Git decides what exists; Astro decides how it is rendered; Cloudflare decides how it is delivered.

Pages CMS is an editor, not a second source of truth

Pages CMS is an open-source CMS for static sites stored in GitHub. It edits files in the repository directly rather than making the site fetch content from a separate CMS database at runtime.

The important part is the .pages.yml file. It defines which files and collections the editor is allowed to touch and which fields it should show.

On this site I expose:

  • Core pages — selected editorial pages such as the home page and Challenges.
  • Writing — articles in src/content/writing/.
  • Notes — short entries in src/content/notes/.
  • Books — reading notes in src/content/books/.
  • Projects — project metadata and project-page content in src/content/projects/.

The templates, layouts, interactions, and design system remain code. Pages CMS should let me change the content without making it easy to accidentally redesign or break the application.

That means I can create, edit, rename, or delete entries in the collections that allow those operations. A book can be added without creating a new Astro page. A project can be updated without editing a component. A note can be published from a browser. The next Cloudflare build turns the resulting Git commit into the public site.

The order I use when setting it up

I would not start with the CMS.

  1. Put the project in GitHub.
  2. Connect the repository to Cloudflare Pages and get one clean production deploy.
  3. Add the .pages.yml configuration to the repository.
  4. Connect Pages CMS to GitHub and authorize that repository.
  5. Make one harmless content edit in Pages CMS.
  6. Confirm the commit appears in GitHub and triggers a successful Cloudflare build.

That sequence isolates problems. If Cloudflare cannot build the repository, adding a CMS only gives you a second place to debug.

Receiving email without hosting a mailbox

Cloudflare Email Routing solves a different problem.

I can create an address such as:

hello@bemorin.com

and route incoming messages to an existing Gmail inbox.

hello@bemorin.com
      ↓
Cloudflare Email Routing
      ↓
my existing Gmail inbox

Cloudflare describes Email Routing as a free service for creating addresses on a domain and forwarding them to an existing inbox. It requires the domain to use Cloudflare DNS.

The useful distinction is that routing is not a mailbox. Cloudflare receives the message at the domain edge and forwards it to the destination I configured. I do not need another inbox to check.

Sending is a separate concern

Incoming routing does not automatically give the website an outbound mail system.

For outbound mail I use Resend. After verifying a domain, Resend provides the SPF and DKIM records that authorize it to send on the domain’s behalf. It can be used through its API or as a normal SMTP server.

That produces two outbound paths:

website / n8n / automation
        ↓
      Resend
        ↓
     recipient

and, for human replies:

Gmail
  ↓  Send mail as + external SMTP
Resend SMTP
  ↓
recipient

Gmail supports sending from another address you own through Send mail as. Resend exposes standard SMTP credentials (smtp.resend.com), so the two can be connected without running an SMTP server yourself.

I still treat authentication as infrastructure, not a checkbox. SPF, DKIM, and DMARC should be checked after the DNS changes rather than assumed to be correct because a dashboard turned green.

What is actually free?

“Free website” can be misleading because there is almost always at least a domain involved.

For this architecture, the recurring infrastructure can stay very close to zero while the site is small:

  • Cloudflare Pages has a usable Free plan for static sites.
  • Cloudflare Email Routing is free for forwarding incoming mail.
  • GitHub can hold the public or private repository depending on the account and plan.
  • Pages CMS edits the GitHub-backed content without requiring me to run a CMS backend.
  • Resend has its own quotas and pricing for outbound mail, so I treat it as a separate sending service rather than pretending outbound email is part of the hosting.

The important saving is not the last few euros. It is the number of systems I am responsible for operating.

The trade-off

This setup is excellent when the site is primarily content and the repository can remain the source of truth.

It is a worse fit if dozens of non-technical editors need complicated permissions, real-time collaborative editing, or content that must change without a deployment. At that point, a conventional database-backed CMS may be the simpler system.

For a personal Almanack, I prefer the opposite trade-off: content stays portable, every change is versioned, the public site is mostly static, and the editing interface is replaceable.

Official references