Hugo with Codeberg and BunnyCDN

While enshittification is rolling across a lot of US-based services, let’s try to host our static Hugo page with EU-based services only. Domain, deployment, CDN: we’re going back to the roots - or into a more modern tech-era for European internet services.

Disclaimer:
I never had technical problems with Github or Cloudflare. The switch to move my page to providers based inside the European Union is based on a bunch of actions US-based internet companies did or the US government in general1. I do not want to support them anymore. The services I recommend here are tested and trusted by me. Links to these services use the affiliate programm of that particular services to support me using their services.

I moved my website from using Github + Cloudflare pages to a new setup using Codeberg and Bunny.net CDN. During the last months I already read about tools and providers others are using or recommending. I have settled for the following new providers:

I just need a workflow that consists of these three steps

  • write content,
  • deploy (run through various steps, I can control and modify) and
  • enjoy your website.

I can see, that the following setup is going to last for a long time, as it provides a basic, but extensible architecture with the common things you would expect and probably already enjoy, like

  • insane speed
  • automatic TLS certificate
  • a deploy process you as the owner control mostly yourself.

Domain Offensive

I am with them for several years and never had any issues. The domains work, their UI is rather functional than too much clickly-clicky. I like! You just register with them, payment can be done via Credit Card, PayPal, SEPA and more. They even give you a little credit in case something goes wrong with the payment method selected. In any case, you will keep your domains all time and not getting kicked immediately. Their support was all the time very friendly and supportive.

Codeberg

Codeberg is a non-profit source code hoster from Germany. Compared to Github and others, there is no intention to monetize your code or knowledge2. The service runs on Forgejo, a rewritten fork of Gitea3.

They are still small compared to the big players Github and Gitlab, but already provide all functionality for most of us, to run your software project on it. You have got code hosting, Codeberg actions - which is almost the same as Github actions, an issue tracker, packages, release, user management and further more.

Tip

If you cannot find actions in your repository make sure you enable it first. In the repository settings, visit “Units” and click the “Enable Actions” checkbox.

To deploy your code create a .forgejo/workflows/deploy.yaml file in the root folder of your project. This is a 1-to-1 copy of how you structure workflows in Github. Inside your deploy.yaml you can use the following code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
name: Deploy to BunnyCDN

on:
  push:
    branches:
      - main
  workflow_dispatch:

env:
  HUGO_BASE_URL: https://www.my-site.com
  HUGO_VERSION: 0.147.3

jobs:
  build:
    runs-on: codeberg-tiny
    container:
      image: "hugomods/hugo:exts-${{ env.HUGO_VERSION }}"
    steps:
      - name: Clone the repository
        uses: https://code.forgejo.org/actions/checkout@v4
        with:
          submodules: recursive
          fetch-depth: 0
      - name: Generate static files with Hugo
        env:
          HUGO_ENV: production
        run: |
          hugo \
            --gc \
            --minify \
            --baseURL "${{ env.HUGO_BASE_URL }}" \
            --destination cdn
      - name: Upload generated files
        uses: https://code.forgejo.org/actions/upload-artifact@v3
        with:
          name: cdn-content
          path: cdn/
  deploy:
    needs: [ build ]
    runs-on: codeberg-tiny
    container:
      image: node:23-bookworm
    steps:
      - name: Restore artifact
        uses: https://code.forgejo.org/actions/download-artifact@v3
        with:
          name: cdn-content
          path: cdn/
      - name: Install duck.sh
        run: |
          echo "deb https://s3.amazonaws.com/repo.deb.cyberduck.io stable main" | tee /etc/apt/sources.list.d/cyberduck.list > /dev/null
          apt-key adv --keyserver keyserver.ubuntu.com --recv-keys FE7097963FEFBE72
          apt-get update
          apt-get install duck
      - name: Deploy to Bunny.net
        run: |
          duck -y \
               --username codedge-de-prod \
               --password ${{ secrets.BUNNYNET_DEPLOY_KEY }} \
               --existing overwrite \
               --upload ftps://storage.bunnycdn.com/ ./cdn/
      - name: Purge pull zone
        run: |
          curl -X POST \
               -H "AccessKey: ${{ secrets.BUNNYNET_API_KEY }}" \
               https://api.bunny.net/pullzone/<PULL_ZONE_ID>/purgeCache

I created a 2-step deploy process: first build the files, second deploy to Bunny. When using the workflow from above, have a look at the following variables and adjust to your needs.

  • HUGO_BASE_URL base uri: set this to the URL your page should be run at
  • <PULL_ZONE_ID>: set this to the id of your pull zone at Bunny, we’ll come to that in a moment
  • BUNNYNET_DEPLOY_KEY: this is deploy key, with which you deploy your files to Bunny
  • BUNNYNET_API_KEY: this is the general API key, which is used for purging the pull zone

Important:
BUNNYNET_DEPLOY_KEY and BUNNYNET_API_KEY are two separate keys. The deploy key you can get in your push zone in the FTP & API Access tab. The general API key you need to copy from your Account Settings page.

As you can see, we use duck.sh4 for the deployment, which is done via FTP.

Info

duck.sh: I was not able to find some more lightweight CLI FTP tool to transfer multiple files. Probably some shell wizard would write a script utilizing curl. I’d be happy if you can ping or mention me in the Fediverse if you found some alternative.

At the end of the deploy step the pull zones are purged. This invalidates the cache of Bunny so it is going to fetch the new version, that we just pushed.

Deploy to Bunny.net

The most “work” needs to be done on Bunnys side. What we need is a

  • push zone: the primary region where your content will be uploaded to. Select some place close to where you live. Also select at least one geo replication location to have your content replicated
  • pull zone: this can be one or more zone, where your content is pushed to. I set this up with Frankfurt (EU), Singapore (Asia) and New York (North America). Additionally you can select place in Africa and South America.

Create a push zone

Let’s create our push zone by clicking on Storage on the left side and Add Storage Zone.

The name for the push zone can be freely chosen. The storage tier can be probably left to Standard. Only if you require response times between 1-5ms choose Edge (SSD), which comes with almost 3-times the pricing of the Standard tier. Now set up the geo replication, check the pricing per GB and your done with the first part.

I am currently at $0.025/GB with GEO Replication in Frankfurt, Singapore and New York.

Create a pull zone

You can attach the pull zone by clicking Connect Pull Zone.

Enter the name of the pull zone. Again, set the storage tier to _Standard. Adjust the Pricing Zones to what ever suits your needs and wallet.

When creating the pull zone enter the domain for your site as custom hostname. I entered www.codedge.de, as this should be the domain for my page. You can also go with an ANAME record, like codedge.de as an example for my site, but this is not recommended5. Set Force SSL to true and you are good to go.

Costs

Cost comparison is always a bit tricky. If we compare the costs between self-hosting your site on a VPS, most people would argue, that the VPS can also be utilized for some other things than just hosting a website. That is why I will just put the costs side-by-side - and in my opinion the price for this setup is very very competitive.

I used Hetzner for Hosting and just a general pricing of 1 EUR/month for the domain. Bunny charges a minimum of 1 EUR/month in general up to a certain level of traffic. Have a look at their websites calculator to get a better indication.

VPSOur workflow
Domain1 EUR1 EUR
Hosting3.82 EUR1 EUR

For me the amount of traffice served this is within the 1 EUR/month.

I hope you now enjoy your website being served from the EU :-)

This post was created on and updated on .