How to build a free blog with a built-in editor on Cloudflare

A step-by-step guide using Hugo, Decap CMS, GitLab, and Cloudflare Pages. No Workers, no R2, no terminal.

Why this setup exists

Many “free” CMS templates for Cloudflare run on Workers. A Worker renders every page on request, and the free Workers plan gives each request only about 10 ms of CPU time. A database-backed CMS can easily go over that, and when it does, visitors (and crawlers) get errors and Cloudflare sends you warnings that your Workers hit the CPU limit. The usual fix is to pay.

This guide avoids the problem entirely. Your blog is built ahead of time into plain HTML files, and Cloudflare Pages serves those files. Nothing runs when someone visits, so there is no CPU limit to hit. You still get a proper editor in your browser, so you never have to edit files by hand.

How the pieces fit together

  • Hugo

    turns your posts (Markdown files) into a static website.

  • Decap CMS

    is the editor. It lives at

    /admin/

    on your site and saves your posts and images into your Git repository.

  • GitLab

    stores the repository. It also provides the login for the editor.

  • Cloudflare Pages

    watches the repository, rebuilds the site whenever it changes, and serves the result. Images are served from the same site, so you don’t need R2.

The flow when you publish a post: you click Publish in the editor, the editor saves the post to GitLab, Cloudflare notices the change and rebuilds, and about a minute later the post is live.

Why GitLab and not GitHub? Decap’s GitHub login needs a small helper service, which is usually a Worker. Decap’s GitLab login (called PKCE) works with no server at all.

Before you start

You need:

  • A free Cloudflare account.

  • A free GitLab.com account. The free plan includes private projects, with a limit of 5 users per private top-level group, which doesn’t matter for a personal blog.

  • The starter project: a folder containing a minimal Hugo site and the Decap files. It has this structure:

    hugo.toml

    , the site settings

    content/posts/

    , where posts live

    layouts/

    , the page templates

    static/admin/index.html

    and

    static/admin/config.yml

    , the editor

    static/css/style.css

    , the theme styling

    static/images/uploads/

    , where uploaded images are saved

You don’t need Git installed, and you never open a terminal.

Step 1: Create the GitLab project

  1. In GitLab, choose

    New project

    , then

    Create blank project

    .

  2. Name it, for example,

    my-blog

    .

  3. Set visibility to

    Private

    .

  4. Untick

    Initialize repository with a README

    , or just let GitLab create one. The upload in the next step will replace it either way.

  5. Create the project.

Note your GitLab username and project name. Together they form your repository path, for example your-username/my-blog.

Step 2: Upload the starter files

The easiest method is GitLab’s Web IDE, which supports drag and drop.

  1. Unzip the starter project on your computer.

  2. Open your project in GitLab and press the

    .

    key, or choose

    Code

    , then

    Open in Web IDE

    .

  3. Open the unzipped folder and select everything inside it:

    content

    ,

    layouts

    ,

    static

    ,

    .gitignore

    ,

    hugo.toml

    , and

    README.md

    . Drag the selection into the file list on the left of the Web IDE. Drag the contents, not the outer folder.

  4. If it asks about replacing

    README.md

    , choose to replace it.

  5. Click the

    Source Control

    icon in the left bar, type a message such as

    Initial blog

    , and click

    Commit to main

    .

The files exist only in your browser until you commit. After committing, refresh the project page. You should see content, layouts, static, and hugo.toml listed.

If drag and drop doesn’t work for folders, you can build the structure by hand. Use +, then New directory, to create each folder (typing a path such as static/admin creates both levels), open the folder, and use Upload file to add the files one at a time. The two files named index.html go in different places: one in layouts, one in static/admin.

Step 3: Deploy to Cloudflare Pages

  1. In Cloudflare, open the account-level

    Workers & Pages

    page. Don’t use the Workers Routes page that belongs to a domain.

  2. Click

    Create application

    and look for the

    Pages

    option (a link such as “Looking to deploy Pages? Get started”). You can recognize the right screen by the address ending in

    /create/pages

    .

  3. Choose

    Import an existing Git repository

    , then

    Get started

    .

  4. On the

    GitLab

    tab, click

    Connect GitLab

    , sign in if asked, and click

    Authorize

    . This is required once. Until you do it, your repository won’t appear in the list.

  5. Select your

    my-blog

    repository and click

    Begin setup

    .

  6. Fill in the build settings:Production branch:

    main

    Framework preset:

    Hugo

    Build command:

    hugo

    Build output directory:

    public

  7. Expand

    Environment variables (advanced)

    and add a variable named

    HUGO_VERSION

    with a current Hugo version as the value, for example

    0.139.0

    . Type only the number, with nothing after the last digit.

  8. Click

    Save and Deploy

    .

The first build takes a minute or two. When it finishes, Cloudflare shows your site address, which looks like your-project-xxx.pages.dev. Open it. You should see your blog’s home page.

Step 4: Create the GitLab login for the editor

The editor needs permission to sign in with your GitLab account. You grant that by creating an OAuth application.

  1. In GitLab, click your avatar at the top right and choose

    Edit profile

    .

  2. In the left menu, expand

    Access

    and click

    Applications

    . You can also go directly to

    https://gitlab.com/-/user_settings/applications

    .

  3. Click

    Add new application

    and fill in the form:

    Name:

    anything, for example

    my-blog cms

    Redirect URI:

    https://YOUR-SITE.pages.dev/admin/

    , using your real site address and keeping the slash at the end

    Confidential:

    untick this box

    Scopes:

    tick only

    api

  4. Click

    Save application

    .

  5. Copy the

    Application ID

    , the long string of letters and numbers.

You will also see a Secret. You don’t need it, because this login method doesn’t use one. Don’t paste it into any file or message. If you ever expose it, return to the same page and renew it.

Step 5: Add your settings to the editor config

In GitLab, open static/admin/config.yml, click Edit, and replace the contents with the following. Change the repo and app_id lines to your own values.

Keep the two-space indentation exactly as shown, because YAML depends on it. Commit the change. Cloudflare rebuilds automatically, which takes about a minute.

Step 6: Log in and publish your first post

  1. Open

    https://YOUR-SITE.pages.dev/admin/

    .

  2. Click

    Login with GitLab

    and approve the request.

  3. The editor opens with your posts. Click

    + Posts

    to start a new one.

  4. Add a title, a publish date, an optional description and cover image, and write the body.

  5. Click

    Publish

    , then

    Publish now

    .

The editor saves the post to GitLab, which starts a new Cloudflare build. Follow it on the Deployments tab of your Pages project. About a minute later, the post appears on your site.

Images you upload are stored in static/images/uploads/ in your repository and served from your own site.

Troubleshooting

The build fails with a 404 while downloading Hugo. Look at the log line that names the version it is trying to download. If the version ends with a stray character, such as 0.139.0. with a trailing period, the HUGO_VERSION variable has an extra character. Edit it under the project’s Settings, then Environment variables, so the value is only the version number, then retry the deployment.

Your repository doesn’t appear in Cloudflare. Cloudflare isn’t connected to GitLab yet. On the GitLab tab of the import screen, click Connect GitLab and authorize. If it still doesn’t appear, search by name, reload the page, or revoke Cloudflare under your GitLab applications settings and connect again.

The post is missing from the home page. Hugo hides posts whose date is in the future. This is easy to trigger by accident, since the build time and your post’s time may be in different time zones. Set the date to an earlier time and publish again. If you want future-dated posts to appear, add buildFuture = true to hugo.toml.

The login fails or shows an error. Check that the Redirect URI in your GitLab application matches your site exactly, including https://, the correct domain, /admin/, and the trailing slash. Check that the Confidential box is unticked and that api is ticked. Check that app_id in config.yml is the Application ID and has no extra spaces, and that auth_type is pkce. If you add a custom domain later, add a second redirect URI for it.

You can’t find Applications in GitLab. It sits under Access in the left menu of your profile settings, not at the top level.

The editor works but nothing changes on the site. Check the Deployments tab. A failed build leaves the old version live, and the log will show why.

Limits and things to know

  • Cloudflare’s free plan allows roughly 500 Pages builds per month. Each time you publish, that counts as one build.

  • Publishing isn’t instant. Expect a minute or two for each change to go live.

  • Only people who can write to your GitLab project can log in at

    /admin/

    .

  • Your content lives in the repository as plain files, so you own it and can move it elsewhere at any time.

  • Plans and limits change. Check the Cloudflare and GitLab pricing pages for current figures before relying on them.

What to do next

  • Delete any old Workers you no longer use, so they stop triggering CPU limit warnings. Open the Worker under Workers & Pages, go to

    Settings

    , and scroll to the bottom to delete it. Remove any database it created under

    Storage & Databases

    .

  • Add a custom domain from the Pages project’s

    Custom domains

    tab, and add that domain to your GitLab application’s redirect URIs.

  • Restyle the site by editing

    static/css/style.css

    , or change the page layouts in the

    layouts

    folder.

  • Add pages such as About or Contact by creating new content files or new collections in

    config.yml

    .