Gitea: Lightweight Self-Hosted Git Service Guide
Gitea has become the go‑to choice for teams that want a fast, lightweight Git server without the overhead of heavyweight platforms. Whether you’re running a single‑person project on a Raspberry Pi or managing dozens of repositories for a mid‑size startup, Gitea gives you the flexibility of self‑hosting while staying easy on resources. In this guide we’ll walk through installing Gitea, configuring it for everyday use, and applying a few pro‑tips that keep your instance secure, performant, and ready for real‑world workflows.
Installation
Prerequisites
Gitea runs on any modern Linux distribution, macOS, or Windows, but the most common deployment is on a Linux server. You’ll need at least 1 GB of RAM, 2 CPU cores, and a dedicated user account. If you prefer containerisation, Docker is fully supported, but a binary install gives you the smallest footprint.
Binary installation steps
- Download the latest release from dl.gitea.io.
- Make the binary executable and move it to
/usr/local/bin. - Create a system user
gitwithout login rights. - Set up a data directory, typically
/var/lib/gitea, owned by thegituser. - Configure a
systemdservice to manage the process.
# Download and install
wget -O /tmp/gitea https://dl.gitea.io/gitea/1.22.0/gitea-1.22.0-linux-amd64
chmod +x /tmp/gitea
sudo mv /tmp/gitea /usr/local/bin/gitea
# Create user and directories
sudo adduser \
--system \
--shell /bin/bash \
--gecos 'Git Version Control' \
--group --disabled-password \
--home /home/git \
git
sudo mkdir -p /var/lib/gitea/{custom,data,log}
sudo chown -R git:git /var/lib/gitea
sudo chmod -R 750 /var/lib/gitea
Now add the systemd unit file. This snippet ensures Gitea starts on boot and restarts on failure.
# /etc/systemd/system/gitea.service
[Unit]
Description=Gitea (Git with a cup of tea)
After=network.target
[Service]
User=git
Group=git
Type=simple
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
Enable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable --now gitea
Initial configuration
First run and admin user
Point your browser to http://your-server:3000. The web installer will detect the data directory you created and prompt you for a few settings: database type, repository root path, and the initial admin credentials. SQLite works fine for small teams; PostgreSQL or MySQL are recommended once you exceed a few hundred repos.
Configuration file (app.ini)
After the web installer finishes, Gitea writes /etc/gitea/app.ini. Editing this file lets you fine‑tune everything from SSH port to UI themes. Below is a trimmed example highlighting the most useful sections.
[server]
PROTOCOL = http
DOMAIN = git.example.com
HTTP_PORT = 3000
ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
SSH_PORT = 2222
[database]
DB_TYPE = postgres
HOST = 127.0.0.1:5432
NAME = gitea
USER = gitea
PASSWD = secret_password
SSL_MODE = disable
[repository]
ROOT = /var/lib/gitea/repositories
DEFAULT_BRANCH = main
Restart the service after any change:
sudo systemctl restart gitea
Repository management
Creating a repository via the UI
Log in as the admin you just created, click “New Repository”, and fill out the name, description, and visibility. Gitea automatically creates a bare Git repository on disk and provides both HTTPS and SSH clone URLs.
Using SSH keys
Each user can add an SSH public key under Settings → SSH / GPG Keys. Gitea stores the key and associates it with the user, allowing password‑less pushes. Here’s a quick example of generating a key pair on a developer workstation and adding it to Gitea.
# Generate a new SSH key (press Enter for defaults)
ssh-keygen -t ed25519 -C "alice@example.com"
# Show the public key
cat ~/.ssh/id_ed25519.pub
# Copy the output, then paste it into Gitea's UI
Once added, cloning via SSH looks like:
git clone ssh://git@git.example.com:2222/alice/project.git
Integrations and webhooks
CI/CD pipelines
Gitea’s webhook system can push events to Jenkins, GitLab CI, GitHub Actions (via the repository_dispatch API), or any custom endpoint. A typical CI workflow triggers on push and pull_request events, then runs tests, builds artifacts, and reports status back to the pull request.
- Jenkins: Use the “Git plugin” and configure the Gitea webhook URL
http://jenkins.example.com/gitea-webhook/. - Drone CI: Set the
DRONE_GITEA_SERVERenvironment variable and enable the “Gitea” authentication method. - GitHub Actions: Use the
repository_dispatchendpoint to trigger a workflow from Gitea.
When you create a webhook, Gitea sends a JSON payload. Below is a minimal example of a push event.
{
"ref": "refs/heads/main",
"before": "a1b2c3d4",
"after": "e5f6g7h8",
"repository": {
"id": 42,
"name": "project",
"full_name": "alice/project",
"url": "git@example.com:alice/project.git"
},
"pusher": {
"name": "alice",
"email": "alice@example.com"
}
}
Migration from other Git services
Importing from GitHub or GitLab
If you already have repositories on GitHub, GitLab, or Bitbucket, Gitea can import them directly through the UI. Provide the source URL and an access token with read permissions, and Gitea will clone the repo, preserve issues, and migrate pull requests where possible.
Using the gitea migrate CLI
For bulk migrations, the command‑line tool is far more efficient. The following script pulls a list of repos from a GitHub organization and imports each one into Gitea.
#!/usr/bin/env python3
import subprocess, json, os, sys
ORG = "my-org"
TOKEN = os.getenv("GH_TOKEN")
GITEA_URL = "http://git.example.com"
GITEA_USER = "admin"
GITEA_PASS = "admin_password"
# Fetch repo list from GitHub
repos = json.loads(subprocess.check_output([
"curl", "-s",
f"https://api.github.com/orgs/{ORG}/repos?per_page=100",
"-H", f"Authorization: token {TOKEN}"
]))
for repo in repos:
name = repo["name"]
clone_url = repo["clone_url"]
cmd = [
"gitea", "admin", "repo", "create",
"--owner", GITEA_USER,
"--name", name,
"--private",
"--clone-url", clone_url,
"--username", GITEA_USER,
"--password", GITEA_PASS,
"--url", GITEA_URL
]
subprocess.run(cmd, check=True)
print(f"✔ Imported {name}")
Run the script on a machine with gitea binary in $PATH and network access to both GitHub and your Gitea instance.
Backup and restore strategies
Because a Git server holds the source of truth for your code, backups are non‑negotiable. Gitea stores data in three places: the Git repository files, the SQLite/PostgreSQL database, and the configuration directory.
- Repository files: Use
rsync -a /var/lib/gitea/repositories /backup/gitea-reposnightly. - Database dump: For PostgreSQL, run
pg_dump -Fc gitea > /backup/gitea-db.dump. For SQLite, simply copy thegitea.dbfile. - Configuration: Archive
/etc/gitea/app.iniand any custom templates.
Pro tip: Schedule the backup while the Gitea service is inquietmode (gitea admin maintenance --enable) to guarantee a consistent state, then re‑enable it after the snapshot.
Restoring is a matter of reversing the steps: copy the repository directory back, restore the database, and place the config file in /etc/gitea. Finally, run gitea admin maintenance --disable to bring the server back online.
Performance tuning for large teams
Database optimisation
When you cross the 1 000‑repo threshold, PostgreSQL becomes the preferred backend. Enable pg_stat_statements to monitor slow queries, and increase shared_buffers to 25 % of system RAM. Adding an index on repository.is_private speeds up permission checks for mixed private/public environments.
Git LFS and large files
If your team stores binaries, enable Git Large File Storage (LFS) in app.ini and point it to a dedicated object store (MinIO or S3). This offloads heavy payloads from the main repository directory, keeping clone times low.
- Set
LFS_START_SERVER = truein the[server]section. - Configure
LFS_CONTENT_PATH = /var/lib/gitea/lfsfor local storage. - For S3:
LFS_STORAGE_TYPE = s3and provide the bucket credentials.
Security hardening
TLS termination
Never expose Gitea over plain HTTP in production. Use a reverse proxy like Nginx or Caddy to terminate TLS. Here’s a minimal Nginx snippet that forwards HTTPS traffic to the Gitea backend on port 3000.
server {
listen 443 ssl;
server_name git.example.com;
ssl_certificate /etc/letsencrypt/live/git.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/git.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Two‑factor authentication (2FA) and LDAP
Enable 2FA under Site Administration → Authentication → 2FA Settings. For enterprise environments, configure LDAP or Active Directory so users can sign in with existing credentials. Remember to set REQUIRE_SIGNIN_VIEW = true to hide repository listings from anonymous visitors.
Pro tip: Combine 2FA with IP‑based allowlists in your reverse proxy. This adds a second layer of defence without inconveniencing developers who work from trusted office networks.
Real‑world use cases
- Start‑ups: A single‑node Gitea instance on a cheap VPS provides all the Git features a young team needs, without the cost of GitHub Enterprise.
- Educational institutions: Professors can host class repositories, enforce SSH key policies, and automatically archive student submissions via scheduled backups.
- Embedded device firmware: Because Gitea’s binary is < 15 MB, it can run on edge devices that need to pull firmware updates from a local Git server.
- Compliance‑heavy sectors: With full control over data location, audit logs, and LDAP integration, regulated companies meet internal and external security standards.
Conclusion
Gitea strikes a sweet spot between simplicity and extensibility. By following the installation steps, tweaking the configuration, and applying the performance and security recommendations above, you’ll have a robust self‑hosted Git platform that scales from hobby projects to enterprise workloads. Remember to automate backups, keep your TLS certificates fresh, and periodically review user permissions—these habits turn a lightweight service into a reliable backbone for your development workflow.