Add a CMS to your Jekyll blog for Free With Decap and Cloudflare

Adding a Headless CMS to Your Jekyll Blog

This guide shows you how to add a content management system to your Jekyll blog using Decap CMS (formerly Netlify CMS) and Cloudflare Workers for authentication.

Why Decap CMS?

Decap CMS provides:

  • A user-friendly interface for editing content
  • Git-based workflow (changes are commits)
  • Free and open-source
  • No database required

Step 1: Set Up Decap CMS

Create an admin folder in your project root with two files:

admin/
├── index.html
└── config.yml

admin/index.html

<!doctype html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta name="robots" content="noindex" />
  <title>Content Manager</title>
</head>
<body>
  <script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>
</body>
</html>

admin/config.yml

backend:
  name: github
  branch: main
  repo: your-username/your-repo.github.io

publish_mode: editorial_workflow
media_folder: "assets/img/uploads"
public_folder: "/assets/img/uploads"

collections:
  - name: "blog"
    label: "Blog Posts"
    folder: "_posts"
    create: true
    slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
    fields:
      - {label: "Layout", name: "layout", widget: "hidden", default: "post"}
      - {label: "Title", name: "title", widget: "string"}
      - {label: "Date", name: "date", widget: "datetime"}
      - {label: "Description", name: "description", widget: "string"}
      - {label: "Body", name: "body", widget: "markdown"}
      - {label: "Tags", name: "tags", widget: "list"}
      - {label: "Categories", name: "categories", widget: "list"}

Important: Replace your-username/your-repo.github.io with your actual GitHub repository.

Step 2: Add OAuth Authentication

Since we’re not using Netlify, we need an OAuth provider. We’ll use Cloudflare Workers (free).

Follow Sveltia CMS Auth Setup

  1. Visit Sveltia CMS Auth
  2. Follow the complete tutorial to set up:
    • GitHub OAuth App
    • Cloudflare Worker
    • Environment variables

Key Steps:

  1. Create GitHub OAuth App:

    • Go to GitHub Settings > Developer settings > OAuth Apps
    • New OAuth App
    • Homepage URL: https://yourdomain.com
    • Callback URL: https://your-worker.workers.dev/callback
  2. Deploy Cloudflare Worker:

    • Use the Sveltia template
    • Add your GitHub OAuth credentials as secrets
  3. Update config.yml:

    backend:
      name: github
      repo: your-username/your-repo
      branch: main
      base_url: https://your-worker.workers.dev

Step 3: Test Your CMS

  1. Rebuild your site
  2. Navigate to https://yourdomain.com/admin/
  3. Click “Login with GitHub”
  4. Start creating content!

Tips

  • The editorial workflow creates pull requests instead of direct commits
  • Test locally using npx decap-server before deploying
  • Customize collections to match your site structure
  • Add more content types (pages, projects, etc.) as needed

Troubleshooting

“Error: Failed to load config”

  • Check that config.yml syntax is valid
  • Ensure file paths are correct

“Authentication failed”

  • Verify OAuth app credentials
  • Check Cloudflare Worker is deployed
  • Confirm callback URL matches

Decap CMS

  1. In the root folder, create an admin folder. We’ll add two files there:
admin
├ index.html
└ config.yml

index.html

---
layout: default
title: Admin
permalink: /admin/
subtitle: login screen for decap
title: Login
nav: true
nav_order: 10
dropdown: false
publish_mode: editorial_workflow
media_folder: "assets/uploads"
---

<!doctype html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta name="robots" content="noindex" />
  <title>Content Manager</title>
</head>
<body>
  <!-- Include the script that builds the page and powers Decap CMS -->
  <script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>
</body>
</html>

config.yml

backend:
name: github
branch: main # Branch to update (whatever is your default branch. If left blank it uses master)
repo: your-repo/your-repo.github.io

publish_mode: editorial_workflow
media_folder: "assets/img/uploads"
public_folder: static/media

collections:

- name: "blog" # Used in routes, e.g., /admin/collections/blog
  label: "Blog" # Used in the UI
  folder: "\_posts" # The path to the folder where the documents are stored
  create: true # Allow users to create new documents in this collection
  slug: "{{year}}-{{month}}-{{day}}-{{slug}}" # Filename template, e.g., YYYY-MM-DD-title.md
  fields: # The fields for each document, usually in front matter
  - {label: "Layout", name: "layout", widget: "hidden", default: "post"}
  - {label: "Comments", name: "gisqus_comments", widget: "hidden", default: "true"}
  - {label: "Title", name: "title", widget: "string"}
  - {label: "Publish Date", name: "date", widget: "datetime"}
  - {label: "Description", name: "description", widget: "string"}
  - {label: "Body", name: "body", widget: "markdown"}
  - {label: "Tags", name: "tags", widget: "markdown"}
  - {label: "Categories", name: "categories", widget: "markdown"}
  - {label: "related", name: "related_posts", widget: "hidden", default: "false"}

- name: "news"
  label: "News"
  folder: "posts"
  create: true
  fields:
  - {label: "Layout", name: "layout", widget: "hidden", default: "post"}
  - {label: "Comments", name: "gisqus_comments", widget: "hidden", default: "true"}
  - {label: "Publish Date", name: "date", widget: "datetime"}
  - {label: "Body", name: "body", widget: "markdown"}
  - {label: "gisqus", name: "giscus_comments", widget: "hidden", default: "true"}
    inline: true

This config file tells the CMS that we have two different collections of posts that we want to be able to create. It requires some kinds of fields, and others are conventions that al-folio’s posts already follow. You can add/edit to this list to some degree, like adding another collection for publishing your own book reviews.

Once you rebuild the website with docker-compose, you’ll notice you have a login button on your site. It won’t work out of the box because we’re not on Netlify due to OAuth requirements, so we’ll create one ourselves.

Adding OAuth

Luckily for us, we can run OAuth logic for free with the help of sveltia, a CMS authenticator that relies on an also-free Cloudflare workers script.

Follow this tutorial

Make sure to follow the tutorial exactly, or you won’t be able to make sense of what went wrong.

Conclusion

There ya go. If I’m missing something feel free to request it.

Logo

© 2026 Martin Munguia