February 19th 2025

Version française


Why I’m leaving GitHub

GitHub has been owned by Microsoft since 2018, but while I don’t otherwise like Microsoft or use their products, I didn’t really care. Mainly because I hardly used it. And also because it was before they sucked up all our data to train their AI without asking permission, and before they supported Trump.

More on why I’m leaving all GAFAM services here.

What I was using GitHub for

The years when I spent many hours on open source software (MoreUnit) are long gone now (I know, it’s kind of sad). Event at that time, I’ve been using Sourceforge a lot, and only then GitHub.

Even at work, I stopped using GitHub in 2021.

Today, apart from very occasional pull requests to propose a fix to an OSS project, I sometimes use GitHub to store gists to include in blog articles, and I keep old small projects on it. And I use GitHub Pages to host this blog. That is, I used to. I don’t use it anymore :-)

My needs

As you can see, my needs are pretty basic. I don’t even need GitHub Actions. And of course I can delete some of the projects I keep here, they aren’t of much interest.

So I need a solution to at least host my website (which doesn’t require a git platform), and to occasionally share open source software, even if it’s very simple and sporadic (an example).

And I don’t want to host it by myself.

Alternatives considered

GitLab

To be honest, if I just wanted to replace GitHub with an equivalent service and have a similar experience, I would choose the free plan from GitLab.com. I’ve been using a paid plan for years at work, so I’d feel right at home.

But I don’t particularly like how the product is evolving, or how features are being prioritized (I’ve blogged about the CI part in the past).

And I don’t think the company is ultimately better than anyone else. They’re just constantly trying to catch up to GitHub, whether it’s a good idea or not.

Bitbucket

I don’t know Bitbucket. But I’m sure its free plan would do the job.

That said, it’s made by the people who committed Jira, so… Atlassian also made Confluence and Bamboo, all products that I have used in the past. It’s generally - and understandably - enterprisey, and I’m not especially in love with them.

And again, I don’t think the company is ultimately better than anyone else.

Sourcehut

I find Sourcehut appealing. It’s all open source. They’re concerned with privacy and efficiency. No needless bells or whistles, but packed with features nonetheless. It has web hosting, and a CI platform if I need one. It also has an equivalent to GitHub Gists with https://paste.sr.ht/.

And it would require me to pay $2/month. I’m fine with it.

But it’s still beta, and subject to changes, and why there’s no real risk for me, I don’t want to come back to my setup every now and then because of it.

Sourceforge

Honestly, I haven’t looked into it. I opened their website and it looked like it did when I used it… in 2009. And even then I did not like their interface. So I don’t even know what they offer.

I could go on

There are many git-based platforms out there (and not just git-based), and that’s great! But I haven’t even tried to review all of them. The above selection was enough for me, along with Codeberg, which I will quickly introduce below.

My choice: Codeberg

Codeberg has similarities to Sourcehut, which I mentioned above. It is hosted in Europe, it cares about privacy. It’s open source and supports open source software. In fact, your code has to be open source to be hosted on Codeberg.

It looks like GitHub or GitLab, and features pull requests, an issue tracker, a wiki, etc. It offers very basic web hosting (more on that later). And I can apply for the CI part if I need to… but this is conditioned on the fact that my code is indeed open source, has a clear goal, and will make reasonable use of the CI resources (here are the criteria).

You don’t have to pay to use Codeberg, but it is supported by donations, so it’s best to make one from time to time.

Also, I find that the UI for repositories is similar enough to that of GitHub or GitLab that people won’t be surprised if they receive a link to a project hosted here.

Migrating GitHub repositories to Codeberg

First, you may want to delete the repositories you don’t care about. No need to migrate those, right? I’ve deleted most of mine.

Codeberg offers you to migrate your GitHub repositories automatically, including issues, pull requests, releases, wiki pages, etc. All it needs is an access token with the right permissions.
Just follow the steps documented here.

There is one drawback, however: you have to do it one at a time.

If, like me, you mainly have repositories with nothing else (issues, pull requests, etc.), you can try this script (documentation), which works very well. It will migrate all your repositories at once, or a selection of them.

Migrating Gists to Codeberg

As far as I can tell, Codeberg doesn’t provide a “paste” service or any equivalent to GitHub Gists.

I don’t really mind: a simple repository holding a collection of files can serve the same purpose.

And so I just downloaded all my gists using this tool (GitHub TakeOut), and pushed the result to Codeberg as a new repository:

git clone https://github.com/devarda/github-takeout.git
cd github-takeout
npm install
# firt, create an access token with the right permissions
export GITHUB_TOKEN=...
npm run download-all
mv downloaded-gists ../gists
cd ../gists
git init
git add .
git commit -m "Import gists from GitHub"
# create a repository on Codeberg, and push to it

Migrating a site from GitHub Pages to Codeberg Pages

The principle, simple as hell…

Like GitHub, Codeberg offers to publish:

Unlike GitHub, Codeberg only allows you to publish static files. There won’t be a “magic” Jekyll build for your site. But it’s easy enough to deal with, as I will explain. You can keep your Markdown :-)

Like GitHub, Codeberg Pages allows you to use a custom domain, by creating of a .domains file at the root of your repository (vs a CNAME file), and configuring the proper DNS record(s).

The whole procedure is very well summarized here.

… except when it isn’t

While I was able to create a site for my user using a “pages” repository, I was unable to create one for another repository.

Since I didn’t really need one, I moved on.

How to migrate your site

Disclaimer: I had availability problems with Codeberg Pages, as described later in this article. So you might want to take a look at the Codeberg Service Status page and decide if it’s OK for you before you decide to migrate.

Publication

  1. Create a pages repository on Codeberg, without a first commit.
  2. Locally, pull your “GitHub user” repository (your_name.github.com).
  3. If you relied on Jekyll, rename your branches, because the main one will have to contain the result of a local Jekyll build. Proceed as follows:
    • Install Jekyll (instructions). To my surprise, the latest version (v4.4.1 at the time of writing) worked perfectly to build my old site that relied on a very old version (1.x).
    • From your main branch (or master, or whatever), create a branch for the Jekyll sources of your site. I called mine sources:
      git switch --create sources
      
    • Delete your previous main branch:
      git branch --delete --force main
      
    • Recreate an orphan main branch:
      git switch --orphan main
      
    • Switch back to your sources branch:
      git switch sources
      
    • Add this script to it, and tune it if you like:
      curl --location --remote-name https://codeberg.org/ndemengel/pages/raw/commit/b3e4373ba21884797a0ef7075c3adac444552aac/build-and-publish.sh
      chmod u+x build-and-publish.sh
      git add build-and-publish.sh
      git commit "Add build script"
      
    • Every time you make a change and wants to rebuild your site, run the script:
      ./build-and-publish.sh
      

      It will build the site, commit the result in the main branch, and push this branch to Codeberg.
      One could tune it to also commit and push the sources branch. Or, one could make it a pre-push hook, that would execute it every time the sources is pushed.

  4. Now, wait for your site to be available on your_name.codeberg.page. You can do so by repeatedly running the following command and looking at the result:
    curl --include --silent https://your_name.codeberg.page | head -n 25
    

    You should get a 200 status code, and then the content of your site:

    $ curl --include --silent https://your_name.codeberg.page | head -n 25
    HTTP/1.1 200 OK
    [..]
    <!DOCTYPE html>
      [..]
      <title>Your page title!</title>
    

Using a custom domain

  1. If you want to use a custom domain name, add a .domains file at the root of your main branch, containing your domain name (or multiple ones). For example, mine is n.gridem.fr.
    Commit, and push your main branch.
    Note: if you use the build-and-publish.sh script presented above, place the .domains file in the sources branch, and the script will copy it to main when run.
    Again, wait for the changes to be visible, by running:
    curl --include --silent https://your_name.codeberg.page | head -n 25
    

    You should get a redirection, with a 307 status code:

    $ curl --include --silent https://your_name.codeberg.page | head -n 25
    HTTP/2 307 
    [..]
    location: https://your.domain.name/
    [..]
    <a href="https://your.domain.name/">Temporary Redirect</a>.
    
  2. It is now time to update your DNS record for your.domain.name, to make it an alias of your_name.codeberg.page.
    Since I use a subdomain, all I had to do was changing my CNAME record for n.gridem.fr from ndemengel.github.io to ndemengel.codeberg.page.
    If you use an apex domain, you will have to update (or create) both an ALIAS (or A & AAAA) record and a TXT one, as described on https://codeberg.page/.
    To observe your changes, you can use the dig command, as follows:
    dig your.domain.name +nostats +nocomments
    

    For a CNAME record, you want to see something like the following:

    $ dig autoconfig.gridem.fr +nostats +nocomments 
    
    [..]
    your.domain.name.	60	IN	CNAME	your_name.codeberg.page.
    
    

Note: I advise you to use a short TTL (time to leave) for your DNS records at first, like 60 seconds, so that if you screw up, your fix will propagate fast enough.

But… Codeberg Pages availability

I decided to migrate last week when Codeberg was under constant DDOS attack. I don’t know if it was related, but the availability of the Codebase Pages was around 80% during that time.

As a result, my site was either super slow to load or simply unreachable.

Of course, I can’t blame them. Firstly, because Codeberg doesn’t owe me anything. And also because my thoughts were with them. These people had a tough time!

But I decided to temporarily publish my website on a very small space (10MB!) that I have at Infomaniak. At the time of writing, it is still there, and I will see if I have a new try with Codeberg Pages later.

But the sources are still in my pages repository on Codeberg, and the website is still built and published the same way. Only, I now also copy the build result to Infomaniak.

In the process, I had to change my DNS configuration again (using an A record this time), and tweak a good old .htaccess file too, to expose the contents of a specific folder depending on the subdomain being queried :-)

I also had to update the certificate I use on Infomaniak for my domains (gridem.fr and a few subdomains), to include the n subdomain used by the website.

For some unknown reason, the new certificate took like forever to be live, and so I had to wait for it to be ready before applying my new DNS configuration. Otherwise browsers would complain about the certificate of my site.

In case you ever face the same problem, here is how I monitored the certificate exposed by Infomaniak, to detect when it finally included the new subdomain. This trick relies on the fact that I have a small (and admittedly useless) page already served by Infomaniak, on gridem.fr:

openssl s_client -showcerts -servername gridem.fr -connect gridem.fr:443 <<< "Q" \
  | openssl x509 -text \
  | grep -E -A2 '(Alternative|Validity)'

I just had to wait for the output to have new dates in the “Validity” section, and to see n.gridem.fr in the “Subject Alternative Name” section.

That’s about it

The instructions in this article should be pretty similar for migrating to another service. I hope this experience helps someone!