tags:

tech

sysadmin

forgejo

docker

Migrating Forgejo from native to docker

written by ixhby; published on 2024/08/05; markdown source

Forgejo is a really cool git forge that you can self-host as an alternative to github. That’s exactly why I decided to install it on my VPS.

If you’re here for the migration guide, skip to Installing forgejo the correct way

Installing Forgejo the native way

The official installation guide lists 2 options for installation:

  1. Docker
  2. Download the binary, do some setup and wrap it in a systemd service

Now at the time I didn’t have dockge set up, and didn’t want to deal with docker containers, but downloading straight binaries seemed way worse. At the bottom, they also list a third option:

  1. Install a community maintained package

Ah that’s exactly what I want! Since my VPS runs debian, let’s go install the community maintained debian package and setup forgejo

Problems i had with the debian package

Quick Disclaimer: I don’t blame any of this on the people packaging forgejo for debian. These are issues i had with my personal workflow and it’s not the average admin experience. I also didn’t talk to them about any of this, so if anything I’m at fault here.

Now I had forgejo up and running, but there was still something i wasnt satisfied with.

SSH Users and file permissions

When you clone a git repository, you can choose between 2 download options: HTTP(S) or SSH. With HTTP(S), you create an access token and log in when cloning or pushing using your username and the access token. With SSH, you generate (or already have) an SSH keypair, add the public key to your account, and use the SSH key in git when interacting with the remote. The clone URLs differ slightly when using either method:

git clone https://git.ixhby.dev/ixhbinphoenix/flake.git # HTTP(S)
git clone git@git.ixhby.dev/ixhbinphoenix/flake # SSH

Now there’s a slight annoyance when using the forgejo debian package: The SSH user actually isn’t git by default, it’s forgejo.

        # User   @ server      /repo owner    /repo
git clone forgejo@git.ixhby.dev/ixhbinphoenix/flake

Granted, this is a small nitpick, but it annoys me enough that I wanted to change it. So how do you do that? You change the user that runs forgejo. You’ll need to edit the systemd service installed by the package and change the owner of all forgejo-related things from forgejo:forgejo to git:git. This all works fine until you see a new forgejo release, update the package, and reset all your file permissions and the systemd service. This leads to every update taking about 30 mins extra time, because you forgot the changes you did, wonder why everything is broken, and finally realize what’s wrong and fix it.

Outdated versions

This is a somewhat recent problem, but after the announcement of Forgejo v8, I of course wanted to stay up to date, so I ran apt update aaaaand… nothing. Now this isn’t a problem, even 5 days after the release. What is a problem however, is that after checking my current version, I was still on 1.21.11-2, which was 2 months old at this point. This was the last push to bring me away from the debian package, and I sure as hell wouldn’t go to straight binaries (way too gay for straight binaries :3)

Installing Forgejo the correct way (docker)

As I’ve mentioned before, I’ve transitioned all my service to run through dockge, which is bascially just a managment tool for docker-compose. Sidenote: I really need to make some kind of pre-processor or css class to put the trans flag on all words that start with trans. So, to make things quick this is basically my setup:

services:
  forgejo:
    restart: unless-stopped
    image: codeberg.org/forgejo/forgejo:7 # yes i know v8 is out, didn't get to it yet
    environment:
      - USER_UID=106 # UID of `git` on host
      - USER_GID=113 # GID of `git` on host
    volumes:
      - ./forgejo:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - 3000:3000
    networks:
      - forgejo
    depends_on:
      - postgres
  postgres:
    restart: unless-stopped
    image: postgres:14
    environment:
      - POSTGRES_USER=gitea # legacy reasons lol
      - POSTGRES_PASSWORD=gitea
      - POSTGRES_DB=gitea
    networks:
      - forgejo
    volumes:
      - ./postgres:/var/lib/postgresql/data
networks:
  forgejo:
    external: false

This is almost a 1:1 copy of the forgejo install guide example, which is also why I won’t go into it much. It’s just a container running forgejo and a container running postgres. Note that the postgres URI will now be postgres instead of localhost.

Database migration (PostgreSQL)

Migrating PostgreSQL databases is suprisingly easy, but changes slightly depending on when you installed forgejo and if you made changes regarding the database config.

Run pg_dump -U gitea -W -F t gitea > dump.psql on the host, then start your postgres container and run cat dump.psql | docker exec -it postgres pg_restore -U gitea -d gitea -F t. This assumes your database was previously named gitea, with the gitea user, and that your container is named postgres. What is the t for? No fucking clue! But it works.

Forgejo data migration

The new data location is in /forgejo relative to the compose.yaml. In there are 3 directories: git gitea ssh. git contains the git repositories and LFS, gitea contains all other data related to forgejo and ssh contains the SSH host keys. So, you can basically copy /var/lib/forgejo/data/forgejo-repositories to ./forgejo/git/repositories, and everything else from /var/lib/forgejo/data to ./forgejo/gitea/

If you have a custom folder, copy all of it’s contents into ./forgejo/gitea.

Config files

DO NOT JUST COPY THE CONFIG FILE! You’ll need to at least change the paths defined in it. I recommend putting your old config file as old-app.ini into ./forgejo/gitea/conf/ and comparing the new and old config side-by-side and changing stuff that doesn’t match. I’d also recommend (re-)reading the configuration cheatsheet since they seem to add new stuff between versions (who could’ve thought?)

Now you should be up and running with your forgejo in docker! Well for the most part

SSH Passthrough

Unfortunately, forgejo doesn’t have a guide for this (yet! i may be contributing one) so i’ll rely on the gitea guide for this. Since the first three methods (Shim, SSH/Docker shell with authorized_keys) didn’t work for me for whatever reason, i’ll be focussing on the Docker Shell with AuthorizedKeysCommand method. You should probably look into the other methods first!

Setup

(if it does not already exist) create a git user on the host using useradd -m --system git

(if you used the git user with the debian package) change the user’s home to /home/git using usermod -d /home/git git

Also, add the user into the docker group using usermod -aG docker git (This is why I advise you to look into other methods first lol)

Create the following docker-shell script in the git user’s home directory and make it executable. Replace <container_name> with the name of your forgejo container.

#!/bin/bash
/usr/bin/docker exec -i -u git --env SSH_ORIGINAL_COMMAND="$SSH_ORIGINAL_COMMAND" <container_name> sh "$@"

Set this script as the git user’s login shell using usermod -s /home/git/docker-shell git

Add the following entry to your sshd_config, once again replacing <container_name> with the name of your forgejo container.

Match User git
  AuthorizedKeysCommandUser git
  AuthorizedKeysCommand /usr/bin/docker exec -i -u git <container_name> /usr/local/bin/gitea keys -c /data/gitea/conf/app.ini -e git -u %u -t %t -k %k

Now systemctl restart sshd and you’re done!

Explanation

What’s happening here is when a user connects using SSH, the AuthorizedKeysCommand gets executed to get the authorized_keys of the container. The authorized_keys contains a command="" that’s executed by the git user on the host. Because the git user on the host has the docker-shell login shell, it gets executed inside of the docker container, emulating the normal behavior.

The End

And that’s it! Now you’re running forgejo inside docker, with SSH support using the git user.

Anyways, if you have any questions, contact me on email, fedi or xmpp and have a good day :3