A log of all that I needed to do to restore/migrate my forgejo instance onto a freshly-installed debian VPS.
I recently needed to completely reinstall the VPS I rent from OVH; it would never progress past loading the initramfs and I never managed to figure out why, even when rebooting into OVH's "Rescue Mode" through their web console.
What follows is a recount of the different actions I took to have my old data show up in my "new" forgejo instance. Namely, the (few) user accounts and (many) git repositories that my instance managed.
Following the upgrade guide, I ran the forgejo dump command. This produced a 1.3 GiB zip file:
noah@my-computer$ file forgejo-dump-1765276696.zip
forgejo-dump-1765276696.zip: Zip archive data, at least v2.0 to extract, compression method=store
My instance is configured to use Sqlite for it's database needs (the default), so I didn't need to do anything else to have everything backed up.
podman with systemdThis is where I had the most to learn. As I mentioned in the introduction, my VPS had been running Arch Linux, and I had installed forgejo via the system package manager (pacman). This process had created a forgejo user, set up the necessary folders, and a forgejo.service file that allowed me to simply run systemctl start forgejo to have it working.
Not only is the server now running Debian, but I wanted to have forgejo running inside a container for various reasons (including decoupling the forgejo version I use/run from the version proposed by the system package manager (in this case, apt)).
Following forgejo's "Installation with Docker" page, along with reading podman's online docs and Redhat's "How to run pods as systemd services with Podman" blog post got me most of the way to my goal.
Forgejo's "Installation from binary" page was useful for creating the git user account on my VPS exactly how forgejo expects it to be. It also provided some insight into what the arch linux package had done.
Lastly, the following issues on the forgejo repositories hosted at codeberg were illuminating:
redbeardKeeping docs up-to-date, clear, and meaningful is hard - even for awesome projects like forgejo (especially for volunteer-run projects like forgejo!).
Finally, I had the following 3 files, placed at/in /etc/containers/systemd/users:
forgejo.container[Container]
ContainerName=forgejo
Environment=USER_UID=103 #git
Environment=USER_GID=104 #git
Image=codeberg.org/forgejo/forgejo:13-rootless
Network=forgejo.network
PublishPort=3000:3000
PublishPort=2222:2222
Volume=forgejo-data:/var/lib/gitea
Volume=/etc/localtime:/etc/localtime:ro
[Service]
Restart=always
RestartSec=3
[Install]
WantedBy=default.target
forgejo.network[Network]
NetworkName=forgejo
forgejo-data.volume[Volume]
VolumeName=forgejo-data
Then, I could run systemctl --user daemon-reload to have sytemd generate the proper .service files it would need to run the container as a service. Finally, I could run systemctl --user start forgejo to be greeted with forgejo's installation/setup page at <my-server-host>:3000/!
I could also check that the podman volume has been created:
debian@hostname$ podman volume ls
DRIVER VOLUME NAME
local forgejo-data
Although I had a lot less to learn than in the previous part, this was maybe the scariest part of the process. There were so many little things to be done that I started to think it wouldn't be possible to recover the data through using the actual dump, and that I'd need to recreate and re-import each one of my repos.
unzip dump.zipTo not lose track of things, I created a new folder to unzip into:
debian@hostname:~$ mkdir forgejo-restore
debian@hostname:~$ unzip forgejo-dump-1765276696.zip -d forgejo-restore
...
debian@hostname:~$ ls forgejo-restore
app.ini data forgejo-db.sql repos
app.iniI ended up adjusting the generated app.ini config file instead of attempting to bring over the previous one. The most important things were copying over the value for the JWT_SECRET in the [oauth2] section, and the INTERNAL_TOKEN value in the [security] section. I also set the INSTALL_LOCK value to true in the [security] section.
The new app.ini config file now expects the database file name to be different:
[database]
PATH = /var/lib/gitea/data/gitea.db
So I renamed the database file:
debian@hostname:forgejo-restore$ mv data/forgejo.db data/gitea.db
podman data volumeI needed to run podman unshare beforehand to gain access to the volume's contents.
debian@hostname:~$ cd ~/.local/share/containers/storage/volumes/forgejo-data/
debian@hostname:forgejo-data$ ls -lah
total 12K
drwx------ 3 debian debian 4.0K Jan 18 .
drwx------ 4 debian debian 4.0K Jan 18 ..
drwxr-xr-x 15 100999 100999 4.0K Jan 18 _data
debian@hostname:forgejo-data$ podman unshare
root@hostname:forgejo-data$ ls -lah
drwx------ 3 root root 4.0K Jan 18 .
drwx------ 4 root root 4.0K Jan 18 ..
drwxr-xr-x 15 debian debian 4.0K Jan 18 _data
root@hostname:forgejo-data$ cp -R /home/debian/forgejo-restore/data/* ./_data/
root@hostname:forgejo-data$ chown -R debian:debian ./_data/
root@hostname:forgejo-data$ exit
debian@hostname:forgejo-data$
podman data volumeroot@hostname:forgejo-data$ cp -R /home/debian/forgejo-restore/repos ./_data/git/repositories
Note that the target directory is the one specified in the config file for forgejo, in my case:
[repository]
ROOT = /var/lib/gitea/git/repositories
When I tried to push a new commit to a repo on my instance, I got the following error:
noah@my-computer:some-cloned-repo-of-mine$ git push
Enumerating objects: ..., done.
Counting objects: ..., done.
Compressing objects: ..., done.
Total ..., reused ... (delta ...), pack-reused ... (from ...)
remote: ./hooks/pre-receive.d/gitea: line 3: /usr/bin/forgejo: No such file or directory
To https://<instance-domain>/<username>/<repo-name>.git
! [remote rejected] main -> main (pre-recieve hook declined)
error: failed to push some refs to 'https://<instance-domain>/<username>/<repo-name>.git'
I found the following hooks present, server-side, in each repository managed by forgejo:
pre-receive.d/giteapost-receive.d/giteaupdate.d/giteaproc-receiveThey all take roughly the same shape. In each of them, I needed to make the following change:
- /usr/bin/forgejo hook --config=/etc/forgejo/app.ini <hook-name> <args>
+ /usr/local/bin/gitea hook --config=/var/lib/gitea/custom/conf/app.ini <hook-name> <args>
This can be accomplished with 2 separate sed commands:
sed -i 's/bin\/forgejo/local\/bin\/gitea/' <hook-file>
and
sed -i 's/etc\/forgejo/var\/lib\/gitea\/custom\/conf/' <hook-file>
I combined them with find into a one-liner (still inside the shell launched by podman unshare):
root@hostname:forgejo-data$ find . \
-type f -name gitea -o -name proc-receive \
-exec sed -i 's/bin\/forgejo/local\/bin\/gitea/' {} \; \
-exec sed -i 's/etc\/forgejo/var\/lib\/gitea\/custom\/conf/' {} \;
The next day, while looking for a way to cleanup some old Forgejo Actions logs, I discovered the forgejo admin regenerate hooks [sub-]command in the cli docs. Maybe that does the trick instead of the bash oneliner above, but I would want to look at the source code for this command to be sure.
The Forgejo team recently released version 14, along with several minor updates since:
Just for the bugfixes, it would be good to upgrade to the latest version.
There was a lot of back-and-forth involved just to get to this point, leaving a decent amount of clutter. For example, I initialy copied the data folder from the backup to the wrong level in the data volume's hierarchy. This means there currently are some extra folders cluttering up the data volume. For this to serve as a good reference for future reinstalls, I would like to try again from scratch/a fresh data volume. In theory, the following plan should work out:
podman data volume:
new-forgejo-data.volume fileforgejo.container to use it insteadsystemd by running systemctl --user reload-daemon:3000/ , scrolling down, and clicking Install.
Then, I should be able to place the data from my previous install exactly where it needs to be, in exactly as few steps as necessary. We'll see.
This would be a great time/place to try out the forgejo admin regenerate hooks CLI command I mentioned previously.
I was running the forgejo-runner docker image on my desktop (a much more powerful machine than the cheap VPS that hosts my forgejo instance). This was really nice for auto-building projects whenever I pushed some work to the instance's repo. In theory I should be able to simply re-read the installation guide to check that it all works, but as this instance migration/re-installation shows, in practice things are rarely as simple as the theory would suggest.