This week I migrated my virtual server from Digital Ocean, where I pay in USD, to a host in Australia, where the punishing and ever-worse currency exchange rate doesn't make monthly bills a lottery.
This is the third or fourth time I've gone through a server migration, so it's a little less daunting than it has been in the past, but every time there is a new, exciting problem to tackle. I'm not a professional system administrator so things that might seem basic to others can be quite challenging for me.
This post is about one of those – migrating my self-hosted Forgejo git repository . Forgejo actually has very good documentation, which is less typical than we might hope in FOSS projects. The guide to installation is quite thorough and explains what each step is doing and why it is needed. The guide to upgrading is also pretty good, and includes a section on how to make a backup – they even created a CLI command to do this: forgejo dump
. Unfortunately, how to restore from the backup is left as an exercise for the reader.
At some point in the future someone else is going to want to migrate their Forgejo install from one server to another, and not know how to do it. This blog post is for that person so they don't need to go through as much trial and error as I did. Let's be real – that person is probably me again, two years in the future.
Assumptions and caveats
This guide assumes:
- you are running Forgejo on a Linux server and want to migrate it to another Linux server
- you are using the binary rather than Docker
- you are using sqlite as your database
- you have root access to both servers
Your backup and new file structures may be slightly different, depending on which version you are moving to and from. This is what worked for me.
Step 1: make a backup
On your "old" server, first make a backup of forgejo. Forgejo provides instructions for this but there are a few assumptions made, and they caught me out.
To run a backup, you can use forgejo dump
. However there some conditions required to make this work properly.
First of all, if you followed the official the installation instructions you will be running forgejo with the git
user, and that user will not have a password and not be able to use sudo
. That makes it difficult to run forgejo
commands as directly as you might expect. To run a clean dump
we need to:
- run the command as the
git
user withsudo -u git forgejo command --argument
- run
dump
from a directory where thegit
user has write access (the fact you're usingsudo
won't override this requirement) - explicitly declare both the
config
location and theworking-path
- nominate a
tmp
directory thatgit
has permission to write to - declare your database type
The default temporary file location is /tmp
, which probably is owned by root
, so you can create a directory to use instead:
sudo -u git mkdir /home/git/tmp
Then move into the git
home directory so your git
user can save the export file:
cd /home/git
Now you should be able to run a dump with a command like this:
sudo -u git forgejo dump -c /etc/forgejo/app.ini -w /var/lib/forgejo/ -t /home/git/tmp -d sqlite3
You can find out more about what these flags do with:
sudo -u git forgejo dump --help
You should now have a file called something like forgejo-dump-1744939469.zip
.
Step 2: move your backup
You may have your own system worked out for transfering files between your old and new server. If you haven't worked this out yet and your files are not enormous, an easy way to do it is to use scp
with your local machine as an intermediary.
To do this successfully, you need a user with the same name on all three machines, with permission to read and write all the files you're moving. On your old server, move the zip file out of the git
user's directory and into your main user's home directory, then change the ownership:
sudo cp forgejo-dump-1744939469.zip /home/hugh/
sudo chown hugh:hugh /home/hugh/forgejo-dump-1744939469.zip
Now on your local machine, you can scp
from the old server to the new one, via your local machine:
scp old_server:~/forgejo-dump-1744939469.zip new_server:~/
This might take a few minutes, depending on how big your git repositories are.
Step 3: Reroute your DNS
We need to see the web configuration screen in a moment. You could do this by viewing it at [your_ip_address]:3000
, depending on how you have set up your web server configuration. But given you have to redirect your DNS from the old server to the new one anyway, it's probably easier to do it now. Hopefully you remembered to reduce the TTL
value for your (sub)domain earlier in the week 😉. How you do this depends on how you are managing DNS, so it's outside the scope of this post. Don't forget to use certbot
to create a new HTTPS certificate.
Step 4: Install Forgejo on your new server
Before we can import the backup into our new server, we need to set up Forgejo. Follow the installation instructions up to the point where you are looking at the fresh web-based configuration screen. You should finalise installation by selecting sqlite
as your database type, and creating an admin user. It doesn't actually matter what credentials you give your admin user here because we're about to overwrite them, but you need to perform this step in order to get all your directories in order.
Step 5: Restore your backup
Now we can finally actually migrate our data!
You have a zip file sitting in your main user's home directory. We need get these files into the right places with the right permissions.
First of all, disable your forgejo daemon:
sudo systemctl stop forgejo.service
Now we need to unzip the backup. You might need to install unzip
first, and it's a good idea to unzip in a new directory:
sudo apt install unzip
mkdir forgejo-backup
cd forgejo-backup
unzip forgejo-dump-1744939469.zip
You should now have a bunch of files like this:
app.ini
custom
data
forgejo-db.sql
repos
Move the app.ini
file and change ownership:
sudo mv app.ini /etc/forgejo/
sudo chown -R root:git /etc/forgejo
Your data
directory doesn't include everything needed in that directory, so instead of copying over the top of the whole thing, we just copy in what we have:
sudo mv -r data/* /var/lib/forgejo/data/
Our repositories are in the repos
directory from the backup, but we need to copy them in to /data/forgejo-repositories
:
sudo mv -r repos/* /var/lib/forgejo/data/forgejo-repositories/
Now move the custom
directory into /var/lib/forgejo
:
sudo mv -r custom /var/lib/forgejo/
You might be wondering what to do with forgejo-db.sql
- isn't that your database? Turns out it is not! Your sqlite database is within the data
directory in your backup (as forgejo.db
), so you don't need to move it specifically. You can ignore forgejo-db.sql
.
Step 6: Run doctor
Something is likely to be not quite right at this point, especially if you are also upgrading versions and are missing some newer database tables. You can check this with doctor
:
sudo -u git forgejo doctor check --all -c /etc/forgejo/app.ini -w /var/lib/forgejo/ --log-file /home/git/doctor.log
If there is something wrong, doctor
will suggest how you can fix it – doctor fix
may resolve most or all issues. One of these things is likely to be your server SSH key, which we will come to in a moment.
Step 7: Complete installation and restart Forgejo
Now is a good time to make your file permissions slightly more secure, as per the Forgejo installation instructions. We don't need to write to app.ini
any more, so tighten it up a bit:
sudo chmod 750 /etc/forgejo && sudo chmod 640 /etc/forgejo/app.ini
You should now be able to restart Forgejo, and check in a browser that all is well.
sudo systemctl start forgejo.service
If everything went according to plan, you should now be able to log in all users with their original password and any pre-existing 2FA tokens.
Step 8: Update known_hosts
The last thing you may need to do is update the known_hosts
on any local machines pushing to your hosted git repositories. When we set up Forgejo on the new server and tidied it up with doctor
, we ended up with a new SSH key. Your pre-existing git repositories aren't going to like that because it will no longer match what is in your known_hosts
file and quite correctly you will get an alarming message suggesting a MITM attack is underway. You, of course, know better than this, so you can relax, but you also need to fix the confusion.
A simple way to resolve this is to use ssh-keyscan
to query the public key for your Forgejo instance and automatically save it:
ssh-keyscan git.example.com >> ~/.ssh/known_hosts
Note that this is only safe to do in this situation because you're confident the SSH key changed because you changed it. If you suddenly started getting errors about changed keys in any other situation, you'd definitely want to do some investigation before blindly just updating your known_hosts
.
Congratulations on migrating your self-hosted git server!