Moving from GitLab to Gitea

Recently i posted about how GitLab updates are broken, at least when attempting to self host it and occasionally update it on my VPSes, which necessitated me moving to something better suited to my particular set of circumstances.

Now, at the time of writing this, i've already finished the migration which replaces 90% of what GitLab does for me (though certainly not all of its functionality that others might use) in a more lightweight and more easily manageable deployment format, that's hopefully also slightly less resource hungry.

Thus, i figured that i might just document the process and the outcomes of it all, over a few posts, each of which will focus on particular aspects:

  • interacting with the source code (this article)
  • doing automated CI/CD builds of apps
  • persisting dependencies and container images

Without further ado, let's get going!

Why Gitea

Now, GitLab was a good piece of software overall, however today i'd like to focus on an alternative called Gitea, and why i chose it over GitLab:

00 gitea logo

If you haven't read my article that details my issues with updating GitLab, i should probably let you know that i've been self hosting an instance for a while. But even apart from the problems with updating it, i also ran into some with just running GitLab day to day. For example, it has quite a few components included in the Omnibus install:

01 GitLab lots of components

This wouldn't be much of a problem in of itself, even my own homepage runs Ruby and Rails, though for a variety of reasons (mostly its size and complexity), my instance was pretty resource hungry and attempting to run it with anything under 2 GB of RAM yielded issues with even basic operation:

02 GitLab slow

This is far from ideal circumstances, considering that i was the only (constant) user on the instance, even if there were a few projects there:

03 GitLab lots of projects

It actually got so bad that even pushing code or switching to remote branches was slow and took many seconds, as did suffer the overall stability of the system, hence i looked at alternatives and Gitea seemed to fit the bill pretty well.

Getting Gitea up and running

My plan for the migration was actually pretty simple:

  • create a new domain:
  • launch Gitea there
  • somehow migrate data over from GitLab at
  • switch over the traffic to Gitea once done
  • clean everything up afterwards

The cleanup bit took some time (mostly due to me ending up with 4 GB tar.gz archive of the old GitLab data dir that i needed to pull down through SFTP, just in case), but the rest was actually rather straightforward.

Gitea actually have pretty good documentation about how to launch it in Docker, which helped me greatly. In the end, i settled on the following Docker Compose stack which i launch with Docker Swarm on my own container cluster with a simple bind mount for the data:

version: '3.3'
    image: gitea/gitea:1.16.2
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__server__PROTOCOL=http
      - GITEA__server__ROOT_URL=
      - GITEA__server__SSH_PORT=SOME_VALUE
      - GITEA__mailer__ENABLED=true
      - GITEA__mailer__FROM=SOME_VALUE
      - GITEA__mailer__MAILER_TYPE=smtp
      - GITEA__mailer__HOST=SOME_VALUE
      - GITEA__mailer__IS_TLS_ENABLED=true
      - GITEA__mailer__USER=SOME_VALUE
      - GITEA__mailer__PASSWD=SOME_VALUE
      - SOME_VALUE:22
      - ingress_network
      - ..../gitea/data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
          - node.hostname ==
          memory: 512M
          cpus: '0.75'
    driver: overlay
    attachable: true
    external: true

(for a bit here was replaced by, but you get the idea)

Now, i do also use a reverse proxy, which is why i rewrote the ROOT_URL to use HTTPS since that's what the reverse proxy ensures, even if the connection between the Gitea container and the web server uses regular HTTP, which can also be made safer by encrypting the Docker overlay traffic.

You won't find any of the additional Kubernetes complexity here (even though K3s is pretty nice), just a few simple containers running some simple software, which is why i had an instance up and running minutes later with automatic SSL/TLS:

21 finished

Then, the question of how to actually get the data over from GitLab became relevant.

Migrating GitLab data to Gitea

Admittedly, GitLab has quite a few features and migrating over everything probably wouldn't be feasible. Then again, i don't use all of its features and what was there could thankfully for the most part be migrated in an automatic fashion, thanks to a lovely script that someone more clever than me wrote:

04 migration script

To be honest, it's probably a bit risky to use it for serious matters, unless you audit it in its entirety (which is actually not too hard to do because it's written in Python) and because of it not having been updated in a few years. But you know what they say: "If it's not broken, don't fix it." Certainly something that the people who did Google's UI redesign could have learnt from, if you ask me.

Though, credit where it's due, going in the opposite direction is far easier to do, because GitLab has a nice importer for Gitea.

But the script did what i expected it to as well without too many issues. First, i installed the dependencies it needs:

05 migration script setup

Then, i needed to set up the configuration for it, just a few variables in the script file:

06 migration script config

To allow it to access both instances, i first created a token in GitLab:

07 GitLab token

And another token in Gitea:

08 Gitea token

That was literally all that i needed, after which i could begin the migration:

09 migration process

And after a few minutes, all of the repositories had been successfully moved over, along with groups, public keys and most other configuration that i care about:

10 migration process

One has to actually pause here to appreciate how nice Git is and having interoperability between software like this: some of those Git repos actually originated in a Gogs install, which i ran before switching over to GitLab a number of years ago, in my first year while studying for my Bachelor's in RTU.

These repositories were successfully preserved by me through disk and server failures, moving a few times and also were there all the way through me finishing my Master's degree as well. Here's to having lots of working backups in the future and cool possibilities like these as well!

Inspecting the results

After the script finished its work, i actually reviewed whether everything was successful and it seemed that this indeed was the case (with one exception, however, all of the repos succeeded so that's good enough):

11 import finished

Overall, the repositories looked good, though i don't doubt that having used Git LFS could cause some issues here, as it did with GitLab when mirroring repositories in the past (while it's nice, it's also finnicky, at least in my experience):

12 repositories look good

All of the organizations/groups were also there, as one would expect:

13 organizations migrated

To be honest with you, the UI is similar enough to GitLab and GitHub for this migration to just feel very pleasant and smooth. It's not like i needed to learn an entirely different software package, which is something that sadly can't be said about some other software out there (non linear video editors? writing software?):

14 repo migrated successfully

Even the README files looked okay, with all of the Markdown being rendered as one would expect and all of the images being there:

15 even readme looks okay

Now, one of the things that could be a bit annoying is that the /public path was essentially replaced by /explore/repos which will break the old links, however at the same time it's nice that the ability to browse them is even there and this could easily be solved by a redirect rule in the reverse proxy:

16 public repos also available

With all of that, one could say that all of this migration was successful thanks to that one script, truly an example of good engineering!

Finishing the migration

What was left, was to clean everything up. First, i switched the traffic and configuration over for the instance:

17 move over paths

Now, admittedly, i needed to dig around for a bit in the configuration to get the proxying to work correctly (since with the default config i was redirected to http://localhost:3000 in some paths of the app):

18 have to change additional config

But to their credit, Gitea also has some pretty good documentation on the topic as well:

19 at least the docs are good

Also, i needed to update some of my remote origins for my local Git repos, since the import process sadly wasn't lossless, but thankfully it's not like i had all 84 of them checked out:

22 need to update remote URLs


After that, the rest was surprisingly smooth sailing! Be sure to also read my tutorials on getting Sonatype Nexus and Drone CI running, since those were what i picked to replace GitLab Registry and GitLab CI respectively. But for storing my source code and maybe the occasional merge/pull request and comment, Gitea is an excellent solution that feels infinitely more snappy than GitLab does.

Just look at the memory usage after this migration:

20 way less memory usage

Of course, i'm not saying that everyone out there should attempt to make a switch like i did, but for my circumstances this setup works really nicely!

By the way, want an affordable VPN or VPS hosting in Europe?
Personally, I use Time4VPS for almost all of my hosting nowadays, including this very site and my homepage!
(affiliate link so I get discounts from signups; I sometimes recommend it to other people, so also put a link here)
Maybe you want to donate some money to keep this blog going?
If you'd like to support me, you can send me a donation through PayPal. There won't be paywalls for the content I make, but my schedule isn't predictable enough for Patreon either. If you like my blog, feel free to throw enough money for coffee my way!