In this article I describe how I migrated from Gitea, running on Cloudron with MariaDB as the database, to Forgejo, running on a Kubernetes (K3s) cluster with PostgreSQL as the database. Forgejo is (currently) a drop-in replacement for Gitea. In theory, all what is needed for a migration is to replace the binary. As I'm moving the hosting platform and the database, it's a bit more involved.
How to run Forgejo on Kubernetes, as well as how to copy data around, is out of the scope of this article. I'm using the upstream Helm Chart, which works quite well. You can find the configuration in my GitOps repository for inspiration: ArgoCD application and some additional manifests.
Before starting the migration, I recommend that you get Forgejo up and running with the default installation to make sure it works as intended. This helps to be sure that it works, should something go wrong during the migration, so you can blame the migration, not the installation.
Get data from Gitea
The first thing to do is to export the data from Gitea. Fortunately, there is a specific command, just for that:
gitea dump. The Gitea documentation talks in the section "Backup and Restore" about it. This command even allows dumping the database in a specific format, this helped me to easily move from MariaDB to PostgreSQL. This was the command I used:
I then downloaded this file to my computer for further processing.
Prepare and import database dump
The database dump didn't succeed in importing at the first try. I had to manually resolve one issue in the SQL file: The field
keep_activity_private in the table
user is a boolean. For unknown reasons, the value is set to
NULL in the
INSERT INTO statements, so I manually replaced that value with
Before the data can be imported, make sure that the DB is emptied from the initial installation. I did that with:
# psql --user gitea -d gitea
drop owned by gitea;
create schema public;
After that, I could successfully import the DB dump into PostgreSQL with this standard command:
psql --user gitea -d gitea < /bitnami/postgresql/gitea-db.sql
Migrating the data has to be done carefully because the paths are slightly different in the Forgejo deployment. Especially, the path for the repo is different. First, take note of the directories, then remove all data which was generated by the initial Forgejo installation, and finally move your extracted data from the dump to its place, consulting the notes from the initial installation paths.
The installation with the upstream Helm Chart configures the paths like this:
APP_DATA_PATH = /data
ROOT = /data/git/gitea-repositories
Migrate SSH host keys
I wanted to keep the SSH host keys because the DNS name stays the same after the migration. In Gitea on Cloudron,
sshd is used as the SSH server, but in Forgejo I use the integrated SSH server.
On Cloudron, the keys are in the
sshd/ directory. I copied them to the Forgejo container into the directory
/data/ssh/ and configured the key names specifically. Please note that the private and public keys have to be copied.
SSH_SERVER_HOST_KEYS = ssh/ssh_host_ed25519_key, ssh/ssh_host_ecdsa_key, ssh/ssh_host_rsa_key
Changing to the Forgejo Runner was straightforward: I did a fresh installation of the runner, connected it to Forgejo and removed the Gitea Act Runner.
You can find the deployment manifests in my GitOps repository.
I really like what Forgejo is doing, their emphasis on quality, security and openness makes me feel good to use this software, which is a major differentiation to Gitea (see "Open Letter to Gitea") and means a lot to me. Their monthly update gives me a feeling on the pulse, and it seems to have a well-working pulse and a lovely community.
What I'm looking forward to the most is Forge federation, based on the open Friendly Forge Format (F3) and ActivityPub. There is a blog which elaborates on the progress, which looks fantastic. Even GitLab is implementing Forge federation: ActivityPub support. Maybe one day it will be possible to collaborate on code between many instances? Perhaps we get the decentralization of the Internet a bit further again? That would potentially make the centralization of GitHub more and more uninteresting.