whateverthing.com

Migrating Away from DigitalOcean, Part One

creepy-ass van / puppy monkey baby (2016)

View Full Image »

As part of my plan to move from Digital Ocean to alternative hosting, I need to start picking away at some of the services hosted on my droplets.

I may as well start with the oldest first: I've got a droplet with an uptime of 1300 days. I named it Fridge. It's acting as a static site host for a couple of sites, including this blog. Mostly, though, it exists to be an "apex domain bouncer".

You see, lots of services let you host a Custom Domain on their platform. However, few of them properly handle the www. subdomain versus the "naked domain" or "apex domain".

If users want their site to be example.org, that's fine, but what happen if a browser tries to visit www.example.org? And what about the other way around, where the user wants their domain to be www.example.org and people type in the naked domain name - only to land on an error page?

That's what a bouncer does. It redirects all the lost patrons to the proper domain.

So for me to retire this ancient droplet (running Ubuntu 14.04! What???), I need to find a service that can take over both the Static Site hosting and the Bouncer duties.

And I have! With some creative configuration, I've been able to get codeberg/forgejo and git-pages to take over both roles for me.

Migration Step 0: The Repository Template

I've created a repository template on Codeberg that has all the pieces I need to get up and running.

For simple sites, where the existing repository history doesn't matter, I can create a new repo based off this template and get up and running quite quickly.

More advanced sites are a different beast, because I usually want to preserve the history. In those cases, I can copy the individual files from the template into the existing repository and then push that to Codeberg.

The key file of note is .forgejo/workflows/publish.yaml, which declares that all pushes to main should run two (not just one!) git-pages actions to publish sites.

Step 1: Enable Actions

In order for this to work, Codeberg needs to know that I want to run Actions.

In the site's repository on codeberg, the "Settings" menu has a section called "Units". Under that, "Overview" opens a list of available options.

Here, the "Actions" checkbox needs to be checked on. (Make sure to click Save.)

Step 2: Declare the Domain Variables

Once the Actions tab is visible, Actions -> Variables needs two variables added:

  • TARGET_DOMAIN: This is set to the main URL (e.g., example.org)
  • BOUNCER_DOMAIN: The domain to bounce traffic from (e.g., www.example.org so that the traffic is appropriately redirected to example.org)

Depending on each site's individual needs, this could also be defined the other way around, with the TARGET_DOMAIN containing the "www" and the bouncer domain omitting it.

In the case of one of my domains, I'm actually going to have to define the target domain as an entirely different platform, and set up both hosted domains to bounce off-site. Hopefully that works as intended! (Future Kevin here - it worked!)

Step 3: Update the Redirects File

The repository template includes a bouncer/_redirects file. The contents of the file might look like they're referencing a variable, but this is actually not functional yet - it's a placeholder. Wishful thinking. Future improvement possibility. It would "just" be a "simple" sed -i, after all ... but that's too much for tonight.

Anyway. What this means is that it needs to be replaced with a hard-coded string. By hand. Like a philistine.

Step 4: Add the Content!

At this point, it's time to add the site's content to the docs/ folder. This will likely be different for every site, whether it is hand-coded or generated by a static site generator like Sculpin. It should also be pretty straightforward, so let's move on.

Step 5: Warm Liquid Goo Stage... or DNS

DNS. The lifeblood of the World Wide Web. The warm liquid goo that connects us all together.

When migrating a site, though, things can get rather sticky.

The values you need to know for codeberg + git-pages:

  • CNAME: codeberg.page
  • A / IPv4 records: 217.197.84.141
  • AAAA / IPv6 records: 2a0a:4580:103f:c0de::2

That was pretty much what I expected, but it turned out there's just one little bit left. A TXT record is necessary to authorize the action on the domain.

And because this approach references two domains, that means the record is required for both domains.

For example, for example.org, I had to add a TXT record on the subdomain _git-pages-forge-allowlist.example.org with the value set to the URL of my Codeberg repository (https://codeberg.org/USERNAME/REPO_NAME.git).

Yes, that's a leading underscore, not a Markdown typo.

For the www.example.org domain, I had to do it again: _git-pages-forge-allowlist.www.example.org, pointed at the same repo URL. Note the "www" in this version.

Step 6: Success!

At this point, a commit to the repository should trigger a build. The build should be successful. And the site should be live.

And I should finally be able to unplug the Fridge.

Conclusion

Fuck You, Digital Ocean, for making me have to go through that effort. Stop funding Nazis.

Published: October 16, 2026

Categories: howto

Tags: maintenance, administration, howto, dev, development, deployment