From 6781ef6e48a3f919023ecdbfd096d7ed9e653ff2 Mon Sep 17 00:00:00 2001 From: tomit4 Date: Sat, 30 Aug 2025 18:39:42 -0700 Subject: [PATCH] :memo: Made notes on setting up forgejo --- self_host/forgejo/README.md | 304 +++++++++++++++++++++++++++++++ self_host/forgejo/forgejo_01.png | Bin 0 -> 12470 bytes 2 files changed, 304 insertions(+) create mode 100644 self_host/forgejo/README.md create mode 100644 self_host/forgejo/forgejo_01.png diff --git a/self_host/forgejo/README.md b/self_host/forgejo/README.md new file mode 100644 index 00000000..1b24c55d --- /dev/null +++ b/self_host/forgejo/README.md @@ -0,0 +1,304 @@ +# How to Set Up Forgejo as a Subdomain on Namecheap/Linode using Docker/Nginx + +## Introduction + +I recently went through the small trouble of setting up +[Forgejo](https://forgejo.org/), a Gitea fork released by the team over at +[Codeberg](https://codeberg.org/) and thought I'd document the process for my +own reference and for anyone who wishes to make the attempt in the near future. +Like all forms of technical documentation, this will likely become out of date +as more versions of Forgejo are released. + +For those of you new to the coding scene, but familiar with services like +Github, you will recognize that Github is one of the most user friendly and +ubiquitous version control frontends for Git that there is. That said, as you +become more familiar with the landscape, you will discover other version control +frontends and hosting providers such as [Gitlab](https://about.gitlab.com/), +[Codeberg](https://codeberg.org/), [Sourceforge](https://sourceforge.net/), +[Gitflic](https://gitflic.ru/), and many many others. + +You might also find yourself looking into self hosted options. This is where you +take either a home server or a VPS and host your own front end client for git +and git repositories directly. There are several advantages to this including +complete control over your repositories. You essentially can copy the repos and +take them wherever you go. The code hosted on your repos is your own and is not +subject to the whims of a 3rd part like Microsoft with Github. There are of +course some downsides, of course, with the most obvious of which being +visibility and discoverability, as Github is the de facto place on the web to +find new and interesting pieces of software. + +All this said, I personally have been delving into the world of self hosting a +bit more recently and self hosting my own Git frontend is something I've wanted +to do since my mentor first introduced me to [Gitea](https://about.gitea.com/). +[Forgejo](https://forgejo.org/) is the spiritual successor to Gitea since they +have recently made efforts to commercialize other ventures based off their main +project, hence why I opted to use Forgejo, despite it being much younger of a +project. + +--- + +### Prerequisites + +This document is mainly for myself, but if you happen to have come across this, +and wish to follow along, I'll let you know right of the bat that you'll want to +already have some familiarity with setting up a +[Linode](https://www.linode.com/) or a +[Digital Ocean](https://www.digitalocean.com/) VPS. You of course could set this +up on any VPS that can run Docker and NGINX, but the documentation here will +mainly be using examples from Linode's Client Interface. + +Additionally you'll need to have purchased a top level domain from a Domain +Registrar. I have used [Namecheap](https://www.namecheap.com/). Again, you can +use whichever Domain Registrar you prefer, but the way you interface with your +service might vary. + +Much of these Prerequisites are covered in a +[previous presentation](https://github.com/tomit4/linode_nginx_docker) I gave on +setting up a basic Website/App using Docker, NameCheap, and Linode. Please +consult this document prior to proceeding with this tutorial, as it will be +assumed you already have familiarity with this setup. + +--- + +### Initial Installation + +Firstly, just log into your VPS via SSH. Once there establish a folder where you +will house your docker-compose.yml and the main Forgejo repos. I just dumped +this in my home folder like a slob, but it's probable a cleaner organization +might be in `/usr/local` or `~/.local` or somewhere like that. + +Anyways: + +```sh +mkdir forgejo && cd forgejo +``` + +Within this folder use the default `docker-compose.yml` you can find on +[Forgejo's Docker Installation Page](https://forgejo.org/docs/latest/admin/installation/docker/) + +```yml +networks: + forgejo: + external: false + +services: + server: + image: codeberg.org/forgejo/forgejo:11 + container_name: forgejo + environment: + - USER_UID=1000 + - USER_GID=1000 + restart: always + networks: + - forgejo + volumes: + - ./forgejo:/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "3000:3000" + - "222:22" +``` + +You can change the ports if you like. The `USER_UID=1000` and `USER_GID=1000` +stay the same as they are environment variables for your main `$USER` of your +VPS. Just know that this user cannot be Root. + +You'll also need to create a `git` user on your host VPS. This user just needs +to be able to log in via SSH to your VPS. so: + +```sh +sudo useradd git +``` + +And in the `/etc/ssh/sshd_config` file append the `git` user to the list of +users allowed to ssh in. + +``` +AllowUsers git +``` + +Again, if you have other users that need to login, just append the `git` user to +the end with a space (no new line). + +Now we can return to our `forgejo` directory and use `docker-compose` to pull in +our image and spin up our container: + +```sh +docker-compose up -d +``` + +Again, it's looking for the `docker-compose.yml` file, so just be sure you're in +the directory with said file in it. + +Now you actually should be able to visit the Forgejo repo using your VPS's ip +address, at the port number you exposed (_i.e._ `3000`), and see the initial +instructions: + +![slide forgejo_01](./forgejo_01.png) + +Don't worry about adjusting any of the configurations, we're not going to be +using Forgejo for anything but a personal repo where we are the sole user. + +Just navigate to the bottom and create an admin user. In the case of this +documentation, this will be the only user of our Forgejo Repos, so just keep +that in mind. + +Once done, you should have a basic forgejo installation. + +We're going to get to setting up SSL certs and reverse proxying through NGINX in +a bit, but let's first setup the ability to setup pushing code via SSH. + +First go to your profile icon in the top right-hand side and go to Settings > +SSH/GPG keys. You can find how to create ssh keys +[here](https://docs.codeberg.org/security/ssh-key/). Once you have copied and +pasted the keys in. You're going to want to be sure your `git` user on your VPS +. Simply invoke: + +```sh +cd /home/git && mkdir .ssh && touch authorized_keys +``` + +In `authorized_keys` paste in your ssh `.pub` hash. _Make sure_ it's the `.pub` +hash. Do **not** paste in your private keys. + +Now we should be able to push code to our repos using ssh rather than http. + +That said, we would have to use our raw ip address for all of this at this +point. Test all this out first before proceeding. Create a few repos, push using +ssh and http. Make sure everything works. + +### Setting Up SSL and Reverse Proxying Through NGINX + +This is very similar to the process covered in my +[previous presentation](https://github.com/tomit4/linode_nginx_docker), but +basically we're going to use a subdomain of an existing top level domain I have +in order to setup SSL certs using LetsEncrypt via `certbot`. + +First we'll need to navigate to our Linode Networks Dashboard and add the `git` +subdomain. + +Once logged into the Linode dashboard, just find your tld domain and append a +A/AAAA record that says: + +``` +git.mytld.dev +``` + +Or whatever your TLD name is just prepended with "git." + +Once that is done, it might take 24 hours (but more likely will be +instantaneous) for the DNS records to propagate. You can check if their ready +using + +```sh +dig git.mytld.dev +``` + +If the records are there, you'll see the proper output you'd expect from `dig`. + +Next is to use `certbot` to generate the SSL certs. Since we're using NGINX as a +reverse proxy, we can just generate them with `cerbot` like so: + +```sh +sudo certbot --nginx -d git.mytld.dev +``` + +You can always append `--dry-run` at the end of the command to see if it will +work first (recommended). + +Once the SSL certs are generated and the NGINX configuration is appended. +Navigate to your nginx config. In my case this lives in: + +``` +/etc/nginx/sites-enabled/default +``` + +Here you will find the generated NGINX config. You'll need to edit it to reverse +proxy to the docker containing forgejo. Here we can use the recommended +[Forgejo NGINX config](https://forgejo.org/docs/latest/admin/setup/reverse-proxy/#nginx): + +``` +server { + listen 80; # Listen on IPv4 port 80 + listen [::]:80; # Listen on IPv6 port 80 + + server_name git.example.com; # Change this to the server domain name. + + location / { + proxy_pass http://127.0.0.1:3000; # Port 3000 is the default Forgejo port + + proxy_set_header Connection $http_connection; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + client_max_body_size 512M; + } +} +``` + +The main point of interest is everything in the `location` block. Certbot should +have generated the rest. + +Once this is pasted in you can restart NGINX: + +```sh +systemctl restart nginx +``` + +And now you can visit your `git.mytld.dev` site and it hopefully will work! + +--- + +### Addressing ROOT_URL issue + +Once you login though, you'll notice an error that caused me quite some +frustration. Basically you'll likely see an error regarding the fact that +Forgejo was setup using your VPS's IP address and that it makes no guarantees +about the quality due to the mismatch of what it perceives as a different TLD +being used. We need to change this. + +Now, Forgejo's docs talk about an `app.ini` and anyone who has used Gitea in the +past will be familiar with this, but i t took me many hours to realize the +`app.ini` was not where the documentation as of version LTS(v11.0.3) is +incorrect about where this file lives. It took me embarrassingly long to realize +the generated `app.ini` file resides in the `gitea` directory, and not at the +directory where the docs say it is. So from the `forgejo` directory on your host +machine VPS: + +```sh +cd data/gitea/conf +``` + +And here you will find the `app.ini`. You'll need to change a few things, change +the following fields using your editor of choice: + +```sh +[server] +DOMAIN = git.mytld.dev +SSH_DOMAIN = git.mytld.dev:222 +ROOT_URL = https://git.mytld.dev +``` + +Just restart the docker container: + +```sh +docker container restart forgejo +``` + +And This will rid you of the annoying warning message on login and also make +sure the proper ssh address is provided when opting to create a repository +through which you will mainly be pushing changes via ssh. + +--- + +### Conclusion + +And that's it! My apologies if this document feels a bit off the cuff, but as I +mentioned at the beginning this document is mainly for my own future reference. +If you have any desire to reach out to me though regarding this or any other +tech related topic, please feel free to do so via my +[email](brianhayes.dev@protonmail.com). diff --git a/self_host/forgejo/forgejo_01.png b/self_host/forgejo/forgejo_01.png new file mode 100644 index 0000000000000000000000000000000000000000..f1f200c241adb26ac379a362651b0185df7d611f GIT binary patch literal 12470 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