I just moved from gitea to forgejo and the process could not have been simpler. I'm really impressed. I suppose since the latter was until very recently a soft fork of gitea, I shouldn't be surprised at how easy it was. Still though, I sat down expecting to need to dedicate a few hours to debugging and it "just works" without a hitch after 15 minutes. The most jarring part was getting the actions runner working but the forgejo documentation is great, in fact I'd say better than gitea's docs, so in the end this wasn't a massive issue.

Why?

I have used gitea for hosting personal projects since about 2018 when I first became aware of it. I've been super impressed and I love the fact that it's so lightweight and simple to use. However, I've been a little disappointed by the founders' recent-ish pivot towards commercialisation. Don't get me wrong, I'm an open source maintainer and contributor myself and I know that many of these folks are essentially doing a public service for free. The final straw for me has been the recent announcement of Gitea Enterprise and the direction they seem to be moving in (open "core" with a bunch of paywalled features).

I was a huge fan of Gitlab about 10-11 years ago but they have since heavily commercialised and gotten worse, caring less and less about their community and more and more about corporate user. The open core model can be ok if the features that are paywalled genuinely are things that only big companies care about or if bleeding edge features are open sourced after an embargo period or once a funding milestone has been met. However, I'm once bitten twice shy when it comes to these kinds of precedents in software forges so forgive my cynicism.

Forgejo was set up in response to Gitea's pivot towards commercialisation and their governance model is pretty much set up in a way to prevent the same thing happening again (I guess nothing is impossible but it is unlikely and there would be a lot of drama). For now I will throw in my lot with them.

How to Switch The Server

The server software was the easiest thing to switch. Following the instructions from forgejo, I simply replaced the name of the gitea image with the equivalent forgejo one in my docker-compose.yml and ran docker-compose pull followed by docker-compose up:

Before:

  server:
    restart: unless-stopped
    image: gitea/gitea:1.21
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
    volumes:
     ...

And after:

  server:
    restart: unless-stopped
    image: codeberg.org/forgejo/forgejo:1.21
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
    volumes:
      ...

As soon as I ran this and started the server the whole thing came back up and apart from the new orange paint job, very little had changed about my server.

Swapping the Runner

The runner was slightly more painful because I had made a couple of customisations to my gitea actions runner before in order to get it running docker-in-docker (allowing me to push and pull images from inside the runner without those images appearing in the host system's user space)

  runner:
    restart: unless-stopped
    image: vegardit/gitea-act-runner:dind-latest
    privileged: true
    environment:
      CONFIG_FILE: /config.yaml
      GITEA_INSTANCE_URL: https://git.jamesravey.me
      GITEA_RUNNER_REGISTRATION_TOKEN: ""
      GITEA_RUNNER_NAME: gitea-runner-1
      GITEA_RUNNER_LABELS: "ubuntu-latest,ubuntu-22.04,ubuntu-20.04,ubuntu-18.04"
    volumes:
      - ./runner-config.yaml:/config.yaml
      - ./runner-data:/data:rw
      - ./runner-cache/:/cache
    ports:
      - 42263:42263

Again, the documentation in forgejo is great and they provide an example for running docker-in-docker as a separate service and having the runner talk to it. All I had to do was change a couple of env vars and the runner image name:

  docker-in-docker:
    image: docker:dind
    container_name: 'docker_dind'
    privileged: true
    command: ['dockerd', '-H', 'tcp://0.0.0.0:2375', '--tls=false']
    restart: 'unless-stopped'

  runner:
    restart: unless-stopped
    links:
      - docker-in-docker
    depends_on:
      docker-in-docker:
        condition: service_started
    container_name: 'runner'
    image: code.forgejo.org/forgejo/runner:3.3.0
    user: 1000:1000
    command: forgejo-runner daemon
    environment:
      DOCKER_HOST: tcp://docker-in-docker:2375
      CONFIG_FILE: /config.yaml
      GITEA_INSTANCE_URL: https://git.jamesravey.me
      GITEA_RUNNER_REGISTRATION_TOKEN: ""
      GITEA_RUNNER_NAME: gitea-runner-1
      GITEA_RUNNER_LABELS: "ubuntu-latest,ubuntu-22.04,ubuntu-20.04,ubuntu-18.04"
    volumes:
      - ./runner-config.yaml:/config.yaml
      - ./runner-data:/data:rw
      - ./runner-cache/:/cache
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - 42263:42263

Again, a quick test via docker-compose up and running one of my CI pipelines and voila, everything clicked into place:

a screenshot of a forgejo run with green ticks
Happy green ticks after the forgejo runner completes its run.

Conclusion

If you were thinking about checking out forgejo for philosophical reasons or simply because you were curious and you're worried about switching from gitea, it couldn't be easier. If you are thinking about trying it out though, I'd strongly recommend taking a backup and/or spinning up a temporary or beta site where you can try it out before committing. Also bear in mind that Forgejo recommend not using their actions runner in prod yet because it may not be secure enough. I don't let anyone else use or sign up in my instance and I'm pretty paranoid about what I do run there so I'm taking a calculated risk here.