Gitea on Ubuntu

Through collaboration the need for a private/self-hosted GIT installation arose. After some looking around, I ended up choosing Gitea as it aims to be a painless way for setting up a self-hosted Git service. It is written in Go and distributed as a binary that is cross-platform. I came across several guides to ease the installation process and ended up using some bits of each and some additions of my own. The final product looks like:

  1. Gitea: 1.12.4
  2. MariaDB: 15.1
  3. Nginx: 1.18.0

This was layered with LetsEncrypt to generate valid SSL certificates and fail2ban to automatically remove all the noise that hits publicly accessible ssh daemons. After this initial configuration that we are happy with, I have created a corresponding ansible playbook to automate this for the future.

Add Git user and Insatll Git

We need to add a git system user that gitea will use. This user will have login’s disabled and its system shell set to git-shell. Before adding the user, we must first make git-shell a valid system shell by adding it to /etc/shells.

Add git-shell to /etc/shells

sudo echo "/usr/bin/git-shell" >> /etc/shells

Create git user

Most of the guides I read online set the shell to /bin/bash even though they were disabling passwords. I wanted to restrict this even further by setting it to git-shell. Which is why the first step was to add git-shell to /etc/shells.

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

Install Git

At this point we can install the git service using apt repositories. Some server installations will have this step completed but we will include in our steps anyway.

sudo apt update
sudo apt install -y git

Now we can begin the process of installing and configuring MariaDB for use with Gitea.

Install and Configure MariaDB

Gitea requires a backend database server to store its content (users, repos, code, etc..). MariaDB seems to be a nice fit for our requirements and easily installed and configured. We can quickly install it via apt.

Install MariaDB from the command line

sudo apt install -y mariadb-server mariadb-client

Once complete we want to secure our mysql installation by running mysql_secure_installation. This will address the following (among others):

  1. Entering current root password (if set)
  2. Setting root password
  3. Remove Anonymous users
  4. Disallow root login remotely
  5. Remove test database and access to it
  6. Reloading privilege table

Run mysql_secure_installation

sudo mysql_secure_installation

Answer the questions for each prompt, then we will proceed to the actual configuration of the database Gitea will use.

Creating gitea database

Now that the required packages for gitea to run are installed (git and mariadb) we can proceed to configure the database and then install the actual gitea binary to get the system up and running. Let’s login to mysql:

mysql -u root -p

Once you login using the credentials we set in the previous section, lets create the database that gitea will use.


Now we need to create a database user followed by granting that user the necessary permissions to the “gitea” database.

CREATE USER 'gitea'@'localhost' IDENTIFIED BY 'new_password_here';

Now we grant ALL necessary permissions for user “gitea” to database “gitea”

GRANT ALL ON gitea.* TO 'gitea'@'localhost' IDENTIFIED BY 'user_password_here' WITH GRANT OPTION;

The final step is to change the character set to utf8mb4 with the following command.

ALTER DATABASE gitea CHARACTER SET = utf8mb4 COLLATE utf8mb4_unicode_ci;

Now we will flush privileges to save our changes and exit.


Before moving forward with setting up the gitea file-system environment, we must make a change to the mariadb configuration file to instruct it to use the InnoDB storage engine since this is required by newer versions of Gitea. We need to edit

sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf

Add the following lines to the config file, save and exit. or a small added bonus, a small automation script to tie it all together.

innodb_file_format = Barracuda
innodb_large_prefix = 1
innodb_default_row_format = dynamic

Ctrl+O then Ctrl+X to save and quit respectively.

Gitea Installation

We are now ready to get the Gitea file-system environment set up with necessary directories and permissions. I have automated this process with a simple shell script that you can find at the end of this section, but for the guide, I will illustrate each command.

Download Gitea binary

cd /tmp

Now we must move the binary to its final spot (/usr/local/bin/gitea) and make it executable.

sudo mv gitea-1.12.4-linux-amd64 /usr/local/bin/gitea
sudo chmod +x /usr/local/bin/gitea

Gitea fs environment

Now that we have “installed” the gitea binary, we need to create the relevant directories used by the service and set the permissions accordingly.

sudo mkdir -p /var/lib/gitea/{custom,data,indexers,public,log}
sudo chown git:git /var/lib/gitea/{data,indexers,log}
sudo chmod 750 /var/lib/gitea/{data,indexers,log}
sudo mkdir /etc/gitea
sudo chown root:git /etc/gitea
sudo chmod 770 /etc/gitea

Let’s break down what is happening here.

  1. Create the necessary directories {custom,data,indexers,public,log} within /var/lib/gitea. The -p switch will create the parent directory first if it doesn’t exist {/var/lib/gitea} and the sub-directories as well {custom,data,indexers,public,log).
  2. The chown (change ownership) sets user:group to git:git for the {data,indexers,log} directories.
  3. Set permissions to the {data,indexers,log} directories so that Owner can read/write/execute. Group can read and execute but not write. Others have no access at all.
  4. Create /etc/gitea direcotry
  5. chown (change ownership) to user root group git for directory /etc/gitea
  6. Set permissions for /etc/gitea that Owner and Group can read/write/execute and Others have no access at all.

Now we need to set up a systemd config file to enable gitea at boot and to use systemctl to start/stop/status the gitea web service.

Create gitea.service file

Now we need to create /etc/systemd/system/gitea.service and then reload systemd daemon and see if we are able to stop/start gitea.

sudo nano /etc/systemd/system/gitea.service
Description=Gitea (Git with a cup of tea)

# Modify these two values and uncomment them if you have
# repos with lots of files and get an HTTP error 500 because
# of that
ExecStart=/usr/local/bin/gitea web -c /etc/gitea/app.ini
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
# If you want to bind Gitea to a port below 1024 uncomment
# the two values below


Now we must reload systemd daemon, enable gitea at boot, start it manually and then check its status to verify everything.

sudo systemctl daemon-reload
sudo systemctl enable gitea
sudo systemctl start gitea
sudo systemctl status gitea

You should see something similar when invoking sudo systemctl status gitea

electr0n@vmi421940:~$ sudo systemctl status gitea
● gitea.service - Gitea (Git with a cup of tea)
     Loaded: loaded (/etc/systemd/system/gitea.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2020-10-02 06:26:35 CEST; 2h 21min ago
   Main PID: 683296 (gitea)
      Tasks: 13 (limit: 19149)
     Memory: 156.1M
     CGroup: /system.slice/gitea.service
             └─683296 /usr/local/bin/gitea web -c /etc/gitea/app.ini

At this point, the system is ready to begin the web installation piece by pointing your browser to http://domain:3000/install but we are going to take this a step further by using nginx as a reverse proxy with LetsEncrypt to force HTTPS even when trying HTTP. Let’s get it!!

Nginx / LetsEncrypt

To continue we must first install both nginx and letsencrypt with the following command

sudo apt install -y nginx certbot python3-certbox-nginx

Now we need to make sure nginx is not running by issuing stop to service command.

sudo service nginx stop

Create SSL cert

Run we are ready to run certbot (LetsEncrypt) to issue a standalone SSL certificate to use with our implementation of Gitea.

sudo certbot certonly --standalone -d

Upon successful completion our SSl certificate and key will be in the /etc/letsencrypt/live/ directory.

  1. fullchain.pem = SSL certificate

  2. privkey.pem = SSL private key

Config Nginx to use Certs

Now we must instruct nginx where to find these files and setup the reverse proxy so we can lose :3000 in our URL. Using nano we need to ‘create’ /etc/nginx/sites-available/ with the following content:

server {
    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/;
    ssl_certificate_key /etc/letsencrypt/live/;

    location / {
        proxy_set_header  X-Real-IP  $remote_addr;
        proxy_pass http://localhost:3000;

# Redirect HTTP requests to HTTPS
server {
    listen 80;
    return 301 https://$host$request_uri;

The ‘server’ section instructs Nginx to listen on SSL port 443. Where on the file-system the SSL certificate and private key are for this corresponding domain. The second config section is redirecting / (default GET request) to localhost port 3000 (gitea web service). The final section is enforcing the use of HTTPS using a 301 (redirect). This allows us to only use in our browser vs.

Enable site

Now we create a symbolic link (ln -s} from sites-available to sites-enabled as follows:

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

Now we can start the nginx service for these changes to take affect.

sudo service nginx starting

Now you are ready to point your browser to https:///install to begin the installation process of the gitea web service.

Gitea install

This portion is straight-forward, make sure the db user / pass match accordingly. Setup an admin user for the site and finish it off by clicking on “Install Gitea”.

Gitea post-install

After the initial install, I went ahead and locked down the installation a bit further by making the necessary changes to /etc/gitea/app.ini. I modified the following values:

1. DISABLE_REGISTRATION to true //disable user registration
2. ENABLE_OPENID_SIGNUP to false //disallow openid signup
3. ENABLE_OPENID_SIGNIN to false //disallow openid signin.
4. REQUIRE_SIGNIN_VIEW to true //require sign in to view files
5. DOMAIN to // yours will vary
6. ENABLE_LETSENCRYPT to true //Make sure DOMAIN matches name in ssl_certificate
7. SHOW_REGISTRATION_BUTTON to false // do not show button since we disabled registration.
8. PASSWORD_COMPLEXITY to lower,upper,digit,spec // Must include lowercase, uppercase, digits and special characters.

Some other configuration options to look at for future consideration are:


Check out the cheat-sheet for Gitea. There are a few other options I may look into enabling or disabling but for now, the system is at a point where I am comfortable creating repos and pushing code to the site.