Self hosting is not everyone’s cup of tea

With GitHub repositories becoming the krill for Microsoft’s behemoth AI, and Codeberg still broken, it was time to get around to installing Gitea.

Getting stuff ready

Resources

As usual, I scoured the Internets for several hours and read everything I could find.

However this was kind of pointless as most of the Gitea installation instructions can be found in the official documentation .

Update everything

Firstly, update everything and get ready to install.

sudo apt update && sudo apt upgrade

Then, add a couple of necessary packages (these may already be installed):

sudo apt install wget

sudo apt install git

nginx part I – pre Certbot

I tend to run my nginx setup in three parts:

  • First, a base setup to get things up and running and test that everything works
  • Then, grab a HTTPS Certificate using Certbot
  • Then, add the Certificate and restart nginx

Start setting up the initial nginx conf file by running the following in a Terminal:

sudo nano /etc/nginx/sites-available/GITEA

With the example copied from here :

server {
    listen 80;
    server_name git.example.com;

    location / {
        client_max_body_size 512M;
        proxy_pass http://localhost:3000;
        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;
    }
}

Then, ‘enable’ the site by creating a symlink:

sudo ln -s /etc/nginx/sites-available/GITEA/etc/nginx/sites-enabled/

Check and reload nginx

Then make sure it all works:

sudo nginx -t && sudo systemctl reload nginx

Certbot

I already have a HTTPS site running on this server (you are reading it) so there was no need for me to install extra Certbot software.

I would like the Gitea connection to be over HTTPS too, so for me, adding an extra certificate was a matter of running:

sudo certbot certonly --nginx -d git.example.com

( There is a guide on how to install Certbot however. )

nginx part II – post Certbot

Now that the site is up and running and the Certificate issued, I can replace the earlier one with the following:

(The additional lines come from the excellent Digital Ocean nginx config generator .)

# Upstream server
upstream gitea {
    server 192.168.0.90:3003;
}

server {
    listen                  443 ssl http2;
    listen                  [::]:443 ssl http2;
    server_name             git.example.com;
    # set ;
    root                    /usr/local/bin/gitea;

    # SSL
    ssl_certificate         /etc/letsencrypt/live/git.example.com/fullchain.pem;
    ssl_certificate_key     /etc/letsencrypt/live/git.example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/git.example.com/chain.pem;

    # security
    include                 nginxconfig.io/security.conf;

    # restrict methods
    if ($request_method !~ ^(GET|POST|HEAD)$) {
        return '405';
    }

    location / {
      try_files maintain.html $uri $uri/index.html @node;
    }

    location @node {
      client_max_body_size 0;
      proxy_pass http://localhost:3003;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header Host $http_host;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_max_temp_file_size 0;
      proxy_redirect off;
      proxy_read_timeout 120;
    }
}

# HTTP redirect
server {
    listen      80;
    listen      [::]:80;
    server_name git.example.com;
    include     nginxconfig.io/letsencrypt.conf;

    location / {
        return 301 https://git.example.com$request_uri;
    }
}

Check and reload nginx once more

Then make sure it all still works:

sudo nginx -t && sudo systemctl reload nginx

Setup ufw

Next, Gitea need to be able to be seen from the outside world.

I use ‘ufw’ or ‘ Uncomplicated Firewall ‘ so I need to allow traffic to the Gitea port.

First, see all of the existing connections:

sudo ufw verbose

Then, allow tcp traffic on the Gitea port:

sudo ufw allow 3003/tcp comment "Gitea tcp traffic"

Then, allow udp traffic on the Gitea port:

sudo ufw allow 3003/udp comment "Gitea udp traffic"

Create a user to run Gitea

I prefer to run dedicated processes with a dedicated user.

To add a new user run the following in a Terminal:

adduser \
   --system \
   --shell /bin/bash \
   --gecos 'Git Version Control' \
   --group \
   --disabled-password \
   --home /home/git \
   git

Or:

sudo adduser --system --shell /bin/bash --gecos 'Git Version Control' --group --disabled-password --home /home/git git

Create a Gitea database

Like Certbot I already have MariaDB installed and running as it is the DB behind this WordPress site.

If needed, install by running the following in a Terminal:

sudo apt install mariadb-server -y

However if this is the first time MariaDB has been installed it is a good idea to run through an initial setup in a Terminal:

sudo mysql_secure_installation

To check that it’s running and to start on a reboot, run the following in a Terminal:

sudo systemctl start mariadb

sudo systemctl enable mariadb

sudo systemctl status mariadb

To add the Gitea database run the following in a Terminal:

sudo mysql -u root -p

CREATE DATABASE gitea;
GRANT ALL PRIVILEGES ON gitea.* TO 'gitea'@'localhost' IDENTIFIED BY "super_long_password_goes_here";
FLUSH PRIVILEGES;
QUIT;

Create the required directory structure

Before downloading, Gitea needs a place to live.

Run the following in a Terminal to create the directories:

sudo mkdir -p /var/lib/gitea/{custom,data,log}

Then, own them:

sudo chown -R git:git /var/lib/gitea/

Then, set the appropriate permissions:

sudo chmod -R 750 /var/lib/gitea/

Now create the /etc/ directory:

sudo mkdir /etc/gitea

Then, own it:

sudo chown root:git /etc/gitea

Then, set the appropriate permissions:

sudo chmod 770 /etc/gitea

NOTE: /etc/gitea is temporarily set with write permissions for user git so that the web installer can write the configuration file.

After the installation is finished, it is recommended to set permissions to read-only using:

chmod 750 /etc/gitea

chmod 640 /etc/gitea/app.ini

Download and install

Run the following in a Terminal:

wget -O gitea https://dl.gitea.com/gitea/1.21/gitea-1.21-linux-arm-6

chmod +x gitea

Copy the Gitea binary to a global location

Run the following in a Terminal:

sudo cp gitea /usr/local/bin/gitea

Create a systemd unit file

I want Gitea to start up after a reboot so I create a systemd unit file and enable it.

The following uses wget to grab the latest version from the Gitea repo and saves a copy to the /etc/systemd/system/ folder:

sudo wget https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/systemd/gitea.service -P /etc/systemd/system/

Edit the downloaded file to start on a different port:

sudo nano /etc/systemd/system/gitea.service

By changing:

ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini

To:

ExecStart=/usr/local/bin/gitea web -p 3003 --config /etc/gitea/app.ini

Check and reload Gitea

First, enable, this will ensure that the service restarts after a reboot:

sudo systemctl enable gitea.service

Then, start the service:

sudo systemctl start gitea.service

Then, check the status of the service to see if it is running:

sudo systemctl status gitea.service

Finish installation

In a browser finish setting up the installation.

https://git.example.com

Site Title – Can Be Whatever You Like

Administrator Account Settings:

The first account created will automatically become an Administrator.

User@Example.com

Press the big button, ‘Install Gitea’.

Additional users can now be enrolled or added.

NOTE: There might be a timeout on first start.

Running ‘sudo systemctl start gitea.service‘ again seems to resolve this.

Post Install

As mentioned previously it is recommended to set permissions to read-only using:

sudo chmod 750 /etc/gitea

sudo chmod 640 /etc/gitea/app.ini

Connecting with SSH

NOTE: These steps are done on the local machine that will connect to Gitea, not the server where Gitea is installed.

https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent

Key Management

Create a new key

ssh-keygen -t ed25519 -C "user@example.com"

Generating a key will give it a default name.

I don’t like the default names even though they make sense, so I rename the keys to something I can remember.

mv id_ed25519 gitea

mv id_ed25519.pub gitea.pub

Edit the config

sudo nano ~/.ssh/config

Host                    gitea
User                    git
HostName                git.example.com
IdentityFile            ~/.ssh/gitea
IdentitiesOnly=yes

Restart SSH

sudo systemctl restart ssh

Adding the SSH key to the ssh-agent

Start the ssh-agent in the background:

eval "$(ssh-agent -s)"

Add the SSH private key to the ssh-agent

ssh-add ~/.ssh/gitea

Add the SSH public key to Gitea

Copy the Public Key:

cat ~/.ssh/gitea.pub

Copy the Terminal output

Log into Gitea

Settings | SSH GPG Keys | Add Key

Key Name – Gitea SSH Key

Paste and ‘Add Key’

Connect with SSH

In a Terminal:

ssh gitea

Will return the expected error message:

‘Hi there, User! You’ve successfully authenticated with the key named Gitea SSH Key, but Gitea does not provide shell access.’

Sending to Gitea using SSH

Create a repository in Gitea e.g. ‘Arduino’.

Tick ‘Initialize Repository’ and create.

Initialise locally e.g.

cd /foo/Git/Arduino/

git init

Edit the config to use the SSH Key:

sudo nano /foo/Git/Arduino/.git/config

[remote "origin"]
        url = gitea:User/Arduino.git

Send (force -f) an initial commit:

git add .

git commit -m "Initial Commit"

git push -f origin main

Subsequent pushes using ‘git push -u origin main‘ will be fine.

Tinkering

Turn off registrations

As I noted earlier I am the only user here and only ever will be so there is no sense in having registrations open for other people to sign up to.

So I turn off registrations:

sudo nano /etc/gitea/app.ini

DISABLE_REGISTRATION = true

Disable OpenID sign-ins

Again, I’m the only user so I disable OpenID sign-ins:

[openid]
ENABLE_OPENID_SIGNIN = false
ENABLE_OPENID_SIGNUP = false

Set up Mailer (Mailjet)

In case I bungle something sign-in related, I can use Mailjet to resend a password to a verified email address:

[mailer]
ENABLED = true
SMTP_ADDR = in-v3.mailjet.com
SMTP_PORT = 587
USER = mailjet_user_id
PASSWD = mailjet_user_key
FROM = gitea@example.com

Set the ‘Landing Page’

As noted earlier, I’m the only user here and probably always will be.

It makes no sense to have an incoming browser land on the Gitea homepage, so rather than fiddle with nginx redirects Gitea allows the application to set a ‘landing page’.

In the [server] section, change:

LANDING_PAGE = https://git.example.com/user

Check and reload Gitea once more

Then make sure it all still works.

sudo systemctl restart gitea.service

Then, check the ‘status’.

sudo systemctl status gitea.service

Some scripts

A screenshot of a Gitea repository. The layout will be familiar to anyone who has used GitHub in the past. Gitea Repository Screenshot

Gitea will look familiar to anyone who has used a remote Git repository like GitHub or Codeberg in the past.

However right now my installation is empty; it needs a few repositories migrating to it.

The following two (2) scripts live locally at /foo/Git/ and assume that:

  • A local repository exists at /foo/Git/Arduino/
  • A local repository exists at /foo/Git/Calckey/

Also that:

  • A remote repository exists at git.example.com/User/Arduino
  • A remote repository exists at git.example.com/User/Calckey

(I keep the local repository names the same as the remote repository names to prevent brain ache.)

git_init_everything.sh

The first script loops through all of the directory names in the array and re-initialises them.

It then deletes all of the local history, commits etc.

It then ‘pushes’ the local repository to the pre-existing remote repository.

#!/bin/bash

clear

# Re-initialise and Push everything to Gitea...
echo -e '\e[1;33mRe-initialise and Push everything to Gitea...\e[0m';
echo '';

function initGit {
    local -n arr=$1

    for i in "${arr[@]}"
    do

    # Initialise the local git repo...
    echo -e '\e[1;36mInitialise the local' $i 'git repo...\e[0m';

    # Remove the old repo...
    echo -e '\e[32mRemove the old' $i 'repo...\e[0m';
    cd /foo/$i/;
    sudo rm -R .git;

    # Create the new local repo...
    echo -e '\e[32mCreate the new' $i 'local repo...\e[0m';
    git init;

    # Update the local config file...
    echo -e '\e[32mUpdate the local' $i 'config file...\e[0m';
    echo '[remote "origin"]' >>  .git/config;
    echo '    url = git_gitea:Boffin/'$i'.git' >>  .git/config;

    # Push the local files to the remote repo with an initial commit...
    echo -e '\e[32mPush the local' $i 'files to the remote repo with an initial commit...\e[0m';
    git add .;
    git commit -m "Initial Commit";
    git push -f origin main;

    # Done
    echo -e '\e[1;36m'$i 'done\e[0m';
    echo '';

    done
}

# Define the array of all the directory names
array=(
    "Arduino"
    "Calckey"
    )

# Loop
initGit array

# Everything done
echo -e '\e[1;33mEverything done\e[0m';

git_update_everything.sh

Then the second script loops through all of the directory names in the array and updates them.

It then ‘pushes’ the local repository to the pre-existing remote repository.

#!/bin/bash

clear

# Push everything to Gitea...
echo -e '\e[1;33mPush everything to Gitea...\e[0m';
echo '';

function updateGit {
    local -n arr=$1

    for i in "${arr[@]}"
    do

    # Update the remote git repo...
    echo -e '\e[1;36mUpdate the remote' $i 'git repo...\e[0m'

    cd /foo/$i/
    git add .
    git commit -m 'Modified' .;
    git push -u origin main;

    # Done
    echo -e '\e[1;36m'$i 'done\e[0m'
    echo ''

    done
}

# Define the array of all the directory names
array=(
    "Arduino"
    "Calckey"
    )

# Loop
updateGit array

# Everything done
echo -e '\e[1;33mEverything done\e[0m';

As the traditional spaces people have used to share code and ideas disappear, or become the input for training models, I truly believe that self hosted applications like Gitea can empower us.

So, if you have a website living on a Pi under your telly, there’s no reason you can’t have a git repository living next to it too.