Self-Hosting Git

Debian Development Machine Setup

Chapter 9 ยท Self-Hosting Git with Gitea

Chapter 2 installed the Git client. This chapter installs a Git server: a place on your own network where repositories live, with a web page for browsing them, issues, pull requests and user accounts. We use Gitea, a lightweight self-hosted Git service that ships as a single program. Git and GitHub Foundations teaches the Git commands themselves; here the job is running the service properly on Debian.

Why bother, when GitHub exists?
Your code stays on machines you control, there is no account or limit to worry about, and you learn to run a real service: a dedicated user, a systemd unit, a database, backups. Those skills carry over to everything else you will host. The cost is that you are now responsible for updates and backups, which is why Chapter 10 covers backing this up.

Check First

command -v gitea git --version getent passwd git ss -tlnp | grep -E ':3000|:22'

You want no gitea command, a Git of at least 2.0 (Gitea's documented minimum; Debian 13's is far newer), no existing git user, and nothing else using the ports. If getent prints a line, a git user already exists and you should look at it before continuing.

Choosing a Database

Gitea stores users, issues and settings in a database, while the repositories themselves are ordinary directories on disk. Chapter 7 gave you two candidates.

ChoiceForAgainst
SQLiteNothing extra to run or secure; one file to back up; ideal for one person or a small team.Gitea's own documentation warns it does not scale, so choose something else if the instance may grow.
MariaDBAlready installed from Chapter 7; copes with more users; a familiar tool.One more service that Gitea needs running before it starts, and one more thing to back up.

For a personal development machine, SQLite is the sensible default, and this chapter uses it. The MariaDB steps are given afterwards in case you prefer them. Gitea's documentation lists MariaDB 10.4 or newer as supported, and Debian 13 has 11.8.

Installing Gitea from the Binary

Gitea publishes a ready-built program for Linux, which is the installation method used here. Everything below follows Gitea's official binary-installation guide. Read the version number on the download page first; the examples use 1.27.3, which was the version in the documentation when this chapter was written, so substitute the current one.

Step 1: Download and verify

VER=1.27.3 cd ~/Downloads wget -O gitea https://dl.gitea.com/gitea/$VER/gitea-$VER-linux-amd64 wget -O gitea.asc https://dl.gitea.com/gitea/$VER/gitea-$VER-linux-amd64.asc chmod +x gitea gpg --keyserver hkps://keys.openpgp.org --recv 7C9E68152594688862D62AF62D9AE806EC1592E2 gpg --verify gitea.asc gitea

Look for Good signature from "Teabot <teabot@gitea.io>". This is the same verify-before-trust habit as Chapter 1 and Chapter 8: you are about to run a downloaded program as a service, so confirm it is the one the project signed. A warning that the key is not certified with a trusted signature is normal for a key you have just fetched; a bad signature is not, and means you should delete the file and download again.

Step 2: A dedicated user

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

This creates a git account that cannot log in with a password and exists only to own Gitea's files and run its process. If Gitea is ever compromised, the attacker has that account's permissions and not yours.

Step 3: Directories and the program

sudo mkdir -p /var/lib/gitea/{custom,data,log} sudo chown -R git:git /var/lib/gitea/ sudo chmod -R 750 /var/lib/gitea/ sudo mkdir /etc/gitea sudo chown root:git /etc/gitea sudo chmod 770 /etc/gitea sudo cp gitea /usr/local/bin/gitea

Data lives under /var/lib/gitea, configuration under /etc/gitea, and the program in /usr/local/bin, the place Chapter 3 described for software that did not come from apt. Note that apt knows nothing about this installation, so apt will never update it; upgrades are a manual job you will do in Chapter 10's routine.

The config directory is deliberately loose, for now
/etc/gitea is writable by the git group at this point so the web installer can save its settings. Once setup is finished you tighten it (below). Do not leave it at 770.

Step 4: The systemd service

Gitea provides a sample unit file. Create /etc/systemd/system/gitea.service, keeping to the project's sample and enabling only what you need:

[Unit] Description=Gitea (Git with a cup of tea) After=network.target # If you choose MariaDB, uncomment these two lines so it starts first: #Wants=mariadb.service #After=mariadb.service [Service] RestartSec=2s Type=simple User=git Group=git WorkingDirectory=/var/lib/gitea/ ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini Restart=always Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea [Install] WantedBy=multi-user.target

Read it as three facts: it runs as git, not root; it starts Gitea with the configuration file in /etc/gitea; and Restart=always brings it back if it crashes. The commented lines matter only for a database that runs as its own service.

sudo systemctl enable gitea --now systemctl status gitea journalctl -u gitea -n 30 ss -tlnp | grep gitea

enable --now starts it immediately and at every boot. The status and journal commands are the same ones you would use for any service (systemd in Depth covers them fully), and ss shows which address and port it is listening on.

First-Run Setup

Open the web address of the machine in your browser on port 3000 (Gitea's own documentation refers to port 3000; use whatever ss shows if yours differs), for example http://localhost:3000. The first visit shows an installation page. The fields that matter:

SettingSuggested value
Database typeSQLite3 (the path is offered for you), or MySQL for MariaDB with the details below
Server domaindevserver (or its IP address, which is more reliable if names do not resolve on your network)
Base URLhttp://devserver:3000/, matching how you will actually reach it, because Gitea builds clone links from this
Administrator accountCreate one here, with a strong password. If you skip it, the first account you register becomes the administrator.
If you choose MariaDB
Create the database and user before the installer, using the statements from Gitea's documentation, with the user restricted to localhost (Chapter 7's habit) rather than the '%' wildcard the documentation's example uses for remote access:
CREATE USER 'gitea'@'localhost' IDENTIFIED BY 'choose-a-password'; CREATE DATABASE giteadb CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_bin'; GRANT ALL PRIVILEGES ON giteadb.* TO 'gitea'@'localhost'; FLUSH PRIVILEGES;
In the installer choose MySQL, host 127.0.0.1:3306, and those names. Uncomment the two mariadb.service lines in the unit file.

Lock the configuration down

Once the installer has finished and you can see the Gitea home page, tighten the permissions as the official guide advises:

sudo chmod 750 /etc/gitea sudo chmod 640 /etc/gitea/app.ini sudo systemctl restart gitea

app.ini contains secrets (and a database password if you used MariaDB), so it should be readable only by root and the git group.

Who can reach it?
Gitea listens for web connections on a port anyone on your network can try. Check with ss -tlnp | grep gitea: if it shows 127.0.0.1:3000 it is reachable only from this machine; 0.0.0.0:3000 or *:3000 means the whole network. That is fine on a home network you trust, and worth a second thought anywhere else. Also disable open registration (Site Administration → Configuration, or the installer's optional settings) unless you want strangers making accounts. HTTPS in front of it is outside this course; the HTTPS/TLS Fundamentals and Nginx In Depth courses cover that.

Working With It

SSH keys

Pushing over SSH avoids typing a password each time. In the web page open your profile → Settings → SSH / GPG Keys, and paste the public key (~/.ssh/id_ed25519.pub, the pair you made in Chapter 2). Gitea's documentation describes a built-in SSH server and also the option of using the system's OpenSSH; because Chapter 2 already runs OpenSSH, ask the installer page about SSH settings and read what it offers before changing anything, and keep to the default it proposes. HTTP pushing, described next, needs none of this and is the simplest way to start.

Create a repository and push an existing project

Create an empty repository in the web page (no README, so the histories do not clash), then:

cd ~/projects/composer-demo git status # is it already a repository? git branch -M main git remote add origin http://devserver:3000/YOURNAME/composer-demo.git git remote -v git push -u origin main

git remote add records where the server is; push -u uploads and remembers the link so later a plain git push works. For a project that already has a GitHub remote, use git remote rename origin github first, or use a second name such as gitea, so both remain available. Remember the rule from earlier chapters: commit .gitignore, never venv/, node_modules/ or vendor/.

Using it from other machines

Clone URLs use the Base URL you entered, so the name must resolve from the other computer. If debserver cannot find devserver, use the IP address or add a line to its /etc/hosts. Then git clone http://devserver:3000/YOURNAME/project.git works from there, and VS Code from Chapter 8 can use the repository like any other remote.

Keeping It Healthy

TaskHow
Is it running?systemctl status gitea
What went wrong?journalctl -u gitea, plus the log directory under /var/lib/gitea/log
Restart after a config changesudo systemctl restart gitea
UpgradeDownload and verify the new binary, stop the service, copy it over /usr/local/bin/gitea, start it. Back up first.
Back upChapter 10. Gitea has its own dump command; check gitea dump --help for the options in your version.

Hands-On Exercises

Exercise 1

Install Gitea from the binary: check first, download and verify the signature, create the git user and directories, install the systemd unit, and start it. Show three separate checks that it is running as the git user and listening where you expect.

๐Ÿ“„ View solution
Exercise 2

Complete first-run setup with SQLite, tighten the permissions on /etc/gitea and app.ini, and confirm the service still works afterwards. Explain why the tightening is done after setup and not before.

๐Ÿ“„ View solution
Exercise 3

Push one of your earlier projects (the Composer demo, or a small Python or Node project) to a new Gitea repository. Then clone it into a second directory and prove the copy is identical and that the ignored folders were not uploaded.

๐Ÿ“„ View solution

Chapter 9 Quick Reference

  • Gitea = self-hosted Git service; repositories are directories, everything else is in a database
  • SQLite for a personal machine; MariaDB (11.8 on Trixie, 10.4+ supported) if you prefer
  • Download the binary, verify with gpg --verify (key 7C9E68152594688862D62AF62D9AE806EC1592E2), copy to /usr/local/bin/gitea
  • Dedicated git user via adduser --system ... git; data in /var/lib/gitea, config in /etc/gitea
  • Unit file /etc/systemd/system/gitea.service; systemctl enable gitea --now
  • First run at port 3000: set Base URL to how you reach the machine; then chmod 750 /etc/gitea and chmod 640 /etc/gitea/app.ini
  • Not installed by apt, so apt never updates it: upgrades are manual
  • Push: git remote add origin URL, git push -u origin main
  • Check who can reach it with ss -tlnp | grep gitea; consider disabling open registration