Linux Server setup

Tutorial: Self Hosted Git Server

In this tutorial, we will host a Git service on our Cloud Server.

A Git service can be installed relatively quickly. However, for secure and productive use, further settings are required on the server. In addition, the Git server should be addressed under a subdomain and not take up the entire cloud resource.

The goal of this tutorial is to host Gitea under an encrypted subdomain in a subfolder:

If you want to keep the Git server hidden, you should not make the address too obvious. So it is recommended to choose rather neutral names for the subdomain and the subfolder. In this context, the subfolder contributes significantly more to security, because it would have to be guessed, so to speak. The subdomain, on the other hand, is located in the publicly accessible DNS zone file.

We start by preparing a subdomain with TLS so that we can then install any Git service. As version control software with graphical web interface we take Gitea. It is free and open source. The procedure to implement Gitea is actually clear, but allows many settings in detail. The subtleties you can adjust in the configuration file and via the web interface for you.

Next steps

DNS subdomain record

How to enter a subdomain into the DNS zone file has already been described in Configure DNS records.

As an example I name the subdomain git. However, for reasons mentioned above, it makes sense to assign a name that is not so obvious. The A-record for this would be:

git  IN  A

In my case the final zone file looks like this:


; SOA Records
$TTL 86400
@    IN  SOA (
      2021123100  ; serial
      86400       ; refresh
      10800       ; retry
      3600000     ; expire
      3600        ; negatives caching

; NS Records
@    IN  NS
@    IN  NS
@    IN  NS

; MX Records
@    IN  MX  10 mail

; A Records
@    IN  A
mail IN  A
www  IN  A
dev  IN  A
git  IN  A

Nginx forwarding with HTTPS

In this section, we will proceed similarly to Nginx Subdomain Server Block and Let's Encrypt Certificate for the Subdomain.

This section consists of four steps:

  • Create Nginx server configuration file
  • create empty HTML page
  • Obtain Let's Encrypt certificate
  • Implement TLS in the configuration file

Nginx Server Configuration

We first create an Nginx configuration file:

__$ sudo nano /etc/nginx/sites-available/com.linuxserversetup.git.conf

The contents of com.linuxserversetup.git.conf are preliminary:


server {
  listen      80;
  listen      [::]:80;

  root        /var/www/com.linuxserversetup.git/;
  index       index.htm;

  location / {
    try_files $uri $uri/ /index.htm;

  location = /favicon.ico { access_log off; log_not_found off; }

Create blank HTML page

The empty HTML page should be located at /var/www/com.linuxserversetup.git. We first create the directory path:

__$ mkdir /var/www/com.linuxserversetup.git

And then in it a index.htm:

__$ nano /var/www/com.linuxserversetup.git/index.htm

With the content:


<!doctype html>
    <meta charset="UTF-8">

We associate the configuration file with a symbolic link in the /etc/nginx/sites-enabled folder:

__$ sudo ln -s /etc/nginx/sites-available/com.linuxserversetup.git.conf /etc/nginx/sites-enabled/

And activate the page by checking and restarting Nginx.

__$ sudo nginx -t
__$ sudo systemctl restart nginx

The address should now be accessible with a browser.

Obtain Let's Encrypt certificate

Using Certbot we apply for a certificate from Let's Encrypt:

__$ sudo certbot certonly -d --rsa-key-size 4096

The following dialog starts:

  • Place files in webroot directory (webroot): 2
  • Enter email address
  • Input the webroot for /var/www/com.linuxserversetup.git

The new certificates should then be located under /etc/letsencrypt/live/ More precisely, links to the actual files are located there.

__$ sudo ls -la /etc/letsencrypt/live/

Change Nginx configuration file to HTTPS

We can now implement the certificates in the configuration file we created earlier and make the following additional changes:

  • HTTPS is to be enforced
  • TLS Implementation
  • should reply with the empty HTML page
  • redirects to the internal port 3800

The default port of Gitea is actually 3000. I will change that later to 3800, so here is already the preparation for that.

Open and edit configuration file:

__$ sudo nano /etc/nginx/sites-available/com.linuxserversetup.git.conf

The modified content with the changes will look like this:


# force https
server {
  listen      80;
  return      301 https://$server_name$request_uri;

# main block
server {
  listen      443 ssl http2;
  listen      [::]:443 ssl http2;

  root        /var/www/com.linuxserversetup.git/;
  index       index.htm;

  location / {
    try_files $uri $uri/ /index.htm;

  location /gitea/ {
    proxy_pass http://localhost:3800/;

  location = /favicon.ico { access_log off; log_not_found off; }

  ssl_certificate /etc/letsencrypt/live/;
  ssl_certificate_key /etc/letsencrypt/live/;
  ssl_trusted_certificate /etc/letsencrypt/live/;

  # dhparam
  ssl_dhparam /etc/ssl/certs/dhparam.pem;

  # HSTS
  add_header Strict-Transport-Security "max-age=31536000";

Nginx must be restarted again due to the change:

__$ sudo nginx -t
__$ sudo systemctl restart nginx

This would set up the subdomain and we can start installing from the Git server.

For safety, the address can be checked with the browser.

Install Git

Gitea is dependent on the version control software Git. We install Git with apt.

Update the package sources first:

__$ sudo apt update

Install Git:

__$ sudo apt install -y git

Get Git version:

__$ git --version

Create Git user

A system user git is to be responsible for all version control tasks. We will assign the Gitea system files to this user later.

Create the git user with adduser:

__$ sudo adduser --system --group --disabled-password git

Create database for Gitea

We had already done the MySQL installation in a previous chapter. Now we only need to create a MySQL user gitea with a database of the same name. The password is here example gitea123:

__$ sudo mysql
__gt CREATE USER 'gitea'@'localhost' IDENTIFIED BY 'gitea123';
__gt GRANT ALL PRIVILEGES ON gitea.* TO 'gitea'@'localhost';
__gt exit;

Download and configure Gitea

We obtain Gitea from the official source using wget and store it under /usr/local/bin/gitea. At the time of writing, the current version is 1.16.9:

__$ sudo wget -O /usr/local/bin/gitea

The downloaded file must then be made executable:

__$ sudo chmod +x /usr/local/bin/gitea

We create a folder structure for Gitea under /var/lib. There we also place a folder for custom settings that overwrites the default configuration. This allows Gitea to be updated without resetting the custom settings.

__$ sudo mkdir -p /var/lib/gitea/custom/conf
__$ sudo mkdir -p /var/lib/gitea/data
__$ sudo mkdir -p /var/lib/gitea/log

As mentioned before, the system user git git should be the owner of the gitea files. In addition, we also change the directory permissions to 750.

__$ sudo chown -R git:git /var/lib/gitea/
__$ sudo chmod -R 750 /var/lib/gitea/

Gitea still needs an /etc/gitea system folder. git also owns these files. The directory permissions should be 770 for now.

__$ sudo mkdir /etc/gitea
__$ sudo chown git:git /etc/gitea
__$ sudo chmod 770 /etc/gitea

With the first start the software is installed and thereby further system files are created in /etc/gitea. After that, the directory permissions can be changed again. We will come back to this later.

To make Gitea start automatically, we create a Linux service /etc/systemd/system/gitea.service. A standard file can be found here for example

We create the file:

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

With this content:


Description=Gitea (Git with a cup of tea)
# Don't forget to add the database service dependencies
# If using socket activation for main http/s
# (You can also provide gitea an http fallback and/or ssh socket too)
# An example of /etc/systemd/system/gitea.main.socket
## [Unit]
## Description=Gitea Web Socket
## PartOf=gitea.service
## [Socket]
## Service=gitea.service
## ListenStream=
## NoDelay=true
## [Install]

# Modify these two values and uncomment them if you have
# repos with lots of files and get an HTTP error 500 because
# of that
# If using Unix socket: tells systemd to create the /run/gitea folder, which will contain the gitea.sock file
# (manually creating /run/gitea doesn't work, because it would not persist across reboots)
ExecStart=/usr/local/bin/gitea web --config /var/lib/gitea/custom/conf/app.ini
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
# If you install Git to directory prefix other than default PATH (which happens
# for example if you install other versions of Git side-to-side with
# distribution version), uncomment below line and add that prefix to PATH
# Don't forget to place git-lfs binary on the PATH below if you want to enable
# Git LFS support
# If you want to bind Gitea to a port below 1024, uncomment
# the two values below, or use socket activation to pass Gitea its ports as above


It is basically the default file mentioned above with only two changes:

  • Wants=mysql.service and After=mysql.service are activated.
  • The path to the web configuration file points to your own settings: /var/lib/gitea/custom/conf/app.ini.

Still missing is the web configuration file /var/lib/gitea/custom/conf/app.ini. Gitea has also published a standard app.example.ini file for this purpose. This file contains all possible options, but we do not need to use all of them. Also in the following example file some are superfluous, they should only be entered for a faster postprocessing.

We create the file:

__$ sudo nano /var/lib/gitea/custom/conf/app.ini

With this content:


APP_NAME = Gitea
RUN_USER = git
RUN_MODE = prod

INSTALL_LOCK   = false

DB_TYPE  = mysql
HOST     =
NAME     = gitea
USER     = gitea
PASSWD   = gitea
SSL_MODE = disable
CHARSET  = utf8
PATH     = /var/lib/gitea/data/gitea.db

ROOT = /home/git/gitea-repositories

PROTOCOL         = http
SSH_DOMAIN       =
DOMAIN           =
HTTP_PORT        = 3800
ROOT_URL         =
DISABLE_SSH      = false
SSH_PORT         = 22123
LFS_CONTENT_PATH = /var/lib/gitea/data/lfs
OFFLINE_MODE     = false

ENABLED = true
SENDMAIL_PATH = /usr/sbin/sendmail

REGISTER_EMAIL_CONFIRM            = false
ENABLE_NOTIFY_MAIL                = true
DISABLE_REGISTRATION              = false
ENABLE_CAPTCHA                    = false
REQUIRE_SIGNIN_VIEW               = true
NO_REPLY_ADDRESS                  =

DISABLE_GRAVATAR        = false



MODE      = file
LEVEL     = info
ROOT_PATH = /var/lib/gitea/log


The following parameters must be observed here:

  • all domain names
  • all e-email addresses
  • the HTTP_PORT
  • the SSH_PORT
  • SECRET_KEY, INTERNAL_TOKEN and LFS_JWT_SECRET are generated automatically

We also need to pass the file to the git user.

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

We can start Gitea and also set up the service so that it always starts up when the server is restarted.

__$ sudo systemctl enable gitea
__$ sudo systemctl start gitea

Install Gitea and create administrator

Gitea is now ready to run the first startup, which triggers an installation process by INSTALL_LOCK = false. The options made now at the Web Installer will automatically overwrite the last created configuration file /var/lib/gitea/custom/conf/app.ini. So we call the Gitea web address and create an administrator over it, among other things. The "Email notifications" option can also be enabled at this point (ENABLE_NOTIFY_MAIL). This will notify repository observers about events via email.

One more note: The next section describes how to disable the public registry. This can also be set already now in the installation process via the web interface.

Install Gitea and create administrator:
Gitea Web Installer
Error page after installation

After the installation we land on an error page, which is typical. The page then just needs to be reloaded.

Gitea dashboard
Open Gitea settings
Gitea settings

The Git server is now ready and operational.

If the /etc/gitea folder still exists after installation, which may not be the case in newer Gitea versions, you can lower the access rights of it:

__$ sudo chmod 750 /etc/gitea

Disable public registration

If you don't want to share Gitea publicly, you should enable DISABLE_REGISTRATION registration:

We open the web configuration again:

__$ sudo nano /var/lib/gitea/custom/conf/app.ini

And change the parameter:

Excerpt from /etc/systemd/system/gitea.service


Restart Gitea to apply the changes:


__$ sudo systemctl restart gitea

All settings are documented at Gitea Configuration Cheat Sheet.

Uninstall Gitea

How to uninstall Gitea is described here only for the sake of completeness.

Stop Gitea:

__$ sudo systemctl stop gitea

Output firewall rules:

__$ sudo ufw status numbered

Delete Gitea Firewall rule with number:

__$ sudo ufw delete [RULENUMBER]

Delete Gitea files:

__$ sudo rm -R /etc/gitea
__$ sudo rm /etc/systemd/system/gitea.service
__$ sudo rm -R /var/lib/gitea
__$ sudo rm /usr/local/bin/gitea

Delete system user git:

__$ sudo deluser --remove-home git
__$ sudo groupdel git

Gitea MySQL user and database delete via the MySQL:

__$ sudo mysql
__gt DROP DATABASE gitea;
__gt DROP USER 'gitea'@'localhost';
__gt exit;

Update systemd:

__$ sudo systemctl daemon-reload

Check if the Gitea service still exists:

__$ sudo systemctl status gitea

Useful Gitea commands

Start, stop, restart Gitea, status:

__$ sudo systemctl start gitea
__$ sudo systemctl stop gitea
__$ sudo systemctl restart gitea
__$ sudo systemctl status gitea

systemd protocol from the Gitea service (follow -f):

__$ sudo journalctl -u gitea -f

Gitea Logs (if MODE = file):

__$ sudo ls /var/lib/gitea/log/
__$ sudo less /var/lib/gitea/log/gitea.log
__$ sudo tail -f /var/lib/gitea/log/gitea.log