<!--
.. title: How To: Host a static site in Codeberg Pages
.. slug: how-to-static-site-in-codeberg-pages
.. date: 2026-04-04 18:44:59 UTC-05:00
.. tags: codeberg, git
.. category: tech
.. link: 
.. description: Describe how to use Codeberg Pages to host your static site
.. type: text
-->

## Blogs in 2026

Sometimes we want to passively communicate.

Sometimes we want to document so we don't forget.

Sometimes we don't want to catch up on 8 years of tech to do something simple.

Sometimes we don't want to deal with hosting.

Sometimes we just want to share.

Sometimes we just want static site generators and Codeberg Pages.
<br/>

<!-- TEASER_END -->

## Prepare Codeberg

[Codeberg.org][codeberg] is an web-based Git [Forge] built on [Forgejo],
which is a soft-fork of [Gitea], which was forked from [Gogs]... I think.

They're big on [FOSS] and encourage public repos with permissible licensing.

I use [Creative Commons Attribution-ShareAlike 4.0 International][cc4] in my own projects hosted on Codeberg.

Create an account, and once logged in create a new repository.

**During repository creation**, expand **Advanced settings** and select:

- Default Label Set
- SHA256 Object format
- `main` Default branch

Skip the setup instructions that the repo gives you for now.
<br/>

### Prepare webhook

Codeberg offers [codeberg.page][codeberg-pages] as a free way to host static websites.

There is a CI section available at the bottom of the page if you self-host your own Forgejo instance.

We are going to (mostly) follow the "Set up your website (new method without CI)" method.

Be sure to read through the process, but **for now, only follow Step 2**: set up a webhook as they describe.

The **Target URL** I used for mine was `https://spont.codeberg.page/yahr-matey`, the same URL you visit
to view the blog.
<br/>

## Reorganize Git

We're going to pull some funky branch logic so that:

1. `main` branch contains only source files
1. `pages`, an [orphaned][orphan-branch] branch, contains only generated files

This basically keeps two separate, unrelated versions of your repo; one containing the source used to generate content, the other containing only generated content.

**This approach requires two separate add/commit/push flows**

Since it's basically two different repos pointed to the same origin, we need to perform our add/commit/push workflows in both our project root and the generated content directory.

**Skip this next set of steps if your project has an existing remote repository**

```bash
git init --object-format=sha256 # omit --object-format if you don't use SHA256 hash

git remote add origin git@codeberg.org:user/my_new_blog.git

git checkout -b main

tee .gitignore <<EOF
__pycache__
cache
output
EOF

git add .gitignore
git add .
git commit -m 'init'
git push -u origin main
```

**It is important to add your generated output directory to .gitignore**.

I use [Nikola] so the directory containing my generated content is `output`.

That dir contains the generated files that we _do not_ want included in our repo's `main` branch.

It contains files that we _do_ want in that `pages` branch that we created earlier, so we're essentially going to make it a separate
project that points only at the `pages` branch, and contains only the generated blog contents.

```bash
cd output # Or the generated content directory

git init --object-format=sha256

git remote add origin git@codeberg.org:user/my_new_blog.git

git checkout --orphan pages

# Clear _all_ repo contents on the `pages` branch from git tracking so it only contains `output` contents
git rm -r --cached ../.

# Go back to project and `main` branch so we can generate output to commit
cd ..

# Project dir should still be on `main` but just in case
git checkout main

# Generate blog files
nikola build

# Go back to content repo
cd output

git add .

git commit -m 'first post'

git push -u origin pages
```

<br/>

## Repo Settings

Since `main` contains the source to generate our content, and `pages` contains the content that gets published, we want to make sure to enable [Branch Protection Rules][branch-protection].

The linked docs are for GitHub but they're near identical in Codeberg.

We want to make sure to create a rule for `main` that disallows pushing to it and only allows merge request approval from you.

We need to do the same thing for the `pages` branch.

## Workflow

Now that we've got our git repos configured, the workflow for posting becomes:

1. Make changes to project files
1. Format `.md` files (I use [mdformat])
1. `git add/commit/push` them to Codeberg
1. Create PR to `main`
1. Merge PR
1. `pull` from `main`
1. Generate your new content
1. `cd` to your generated content directory
1. `git add/commit/push` newly generated content to feature branch
1. Create PR to `pages`
1. Merge PR

## Summary

You should see your empty, but rendered, blog if you visit your URL: `https://user.codeberg.page/my_new_blog` pretty quickly, less than a minute in my experience.

You did it, yay!
<br/>

[branch-protection]: https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/managing-a-branch-protection-rule
[cc4]: https://creativecommons.org/licenses/by-sa/4.0/deed.en
[codeberg]: https://codeberg.org/about
[codeberg-pages]: https://codeberg.page/
[forge]: https://en.wikipedia.org/wiki/Forge_(software)
[forgejo]: https://forgejo.org/
[foss]: https://en.wikipedia.org/wiki/Free_and_open-source_software
[gitea]: https://about.gitea.com/
[gogs]: https://github.com/gogs/gogs
[mdformat]: https://pypi.org/project/mdformat/
[nikola]: https://getnikola.com/
[orphan-branch]: https://git-scm.com/docs/git-checkout#Documentation/git-checkout.txt---orphannew-branch
